--- title: "Disassymbl" id: 64536 type: "computer_media" slug: "disassymbl" url: "http://localhost/computer_media/disassymbl/" markdown_url: "http://localhost/computer_media/disassymbl.md" published_at: "2026-03-01T13:06:33+00:00" modified_at: "2026-03-31T07:41:41+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/03/SCR-20260301-hqfh.png" excerpt: "A BASIC-coded Z80 disassembler decodes opcodes from any memory address, handles CB and ED prefixes, and sends formatted mnemonics to both screen and printer." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Ben H. Jackson" slug: "ben-h-jackson" taxonomy: "indiv" url: "http://localhost/indiv/ben-h-jackson/" genre: - name: "Machine Language" slug: "machine-language" taxonomy: "genre" url: "http://localhost/type/machine-language/" media_contents: - id: 60550 title: "Fort Worth TS2068 Club Library Tape" type: "computer_media" url: "http://localhost/computer_media/fort-worth-ts2068-club-library-tape/" media_type: "Program" programmers: - name: "Ben H. Jackson" slug: "ben-h-jackson" taxonomy: "indiv" url: "http://localhost/indiv/ben-h-jackson/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Disassymbl%20%281985%29%28Jackson%2C%20Ben%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2026/03/SCR-20260301-hqfh.png" media_type_tags: "Machine Language" --- # Disassymbl 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`): 1. **Initialisation guard (lines 5–21):** A `REM` header and a deliberate `STOP` prevent accidental `RUN` from line 1, since the mnemonic string arrays must be set up before the disassembler loop begins. 2. **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. 3. **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). 4. **ED-prefix handler (lines 305–390):** Processes `ED`-prefixed two-byte instructions using the B$ mnemonic array and a secondary DATA search. 5. **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)`. 6. **CB-prefix handler (line 700):** Handles `CB`-prefixed bit-manipulation instructions via the C$ array. 7. **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 `DATA` list 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 `DATA` list 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,255` at 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-256` when 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:**`RESTORE` followed by a `FOR`/`READ` loop 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 `FOR` loop at line 130 reads only 26 items, but the `DATA` statement 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 300` for 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 `PEEK` at line 110 stores `PEEK ADDRESS` into a numeric variable also named `PEEK`, shadowing the built-in function name. Subsequent use of `PEEK ADDRESS` in lines 120–125 calls the function, while `IF PEEK=0` and `IF PEEK=237` reference the variable — this is consistent but potentially confusing. - **CB operand index off-by-one:** Line 700 indexes C$ with `PEEK ADDRESS+1` rather than `PEEK ADDRESS`. This appears intentional to convert the 0-based opcode to a 1-based array index, but assumes C$ is dimensioned and loaded accordingly. ## 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 ```