--- title: "mcdis" id: 51980 type: "computer_media" slug: "mcdis" url: "http://localhost/computer_media/mcdis/" markdown_url: "http://localhost/computer_media/mcdis.md" published_at: "2023-08-28T01:10:31+00:00" modified_at: "2026-03-31T18:21:23+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2023/08/mcdis-menu.png" excerpt: "A merge-friendly Z80 disassembler with hex input, a labeled-address dictionary, and a companion machine code block — all packed into a tidy menu-driven toolkit." 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: "Dick Scoville" slug: "dick-scoville" taxonomy: "indiv" url: "http://localhost/indiv/dick-scoville/" genre: - name: "Machine Language" slug: "machine-language" taxonomy: "genre" url: "http://localhost/type/machine-language/" - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Program" programmers: - name: "Dick Scoville" slug: "dick-scoville" taxonomy: "indiv" url: "http://localhost/indiv/dick-scoville/" download_url: "https://archive.org/download/timex-sinclair-software-archive/mcdis%20%28198x%29%28US%29%28TS2068%29%28Cassette%29.zip" images: - url: "http://localhost/wp-content/uploads/2023/08/mcdis-menu.png" - url: "http://localhost/wp-content/uploads/2023/08/mcdis-listing.png" - url: "http://localhost/wp-content/uploads/2023/08/mcdis-cassette.jpg" media_type_tags: "Machine Language, Programming" --- # mcdis This program is a Z80 machine code disassembler and development toolkit designed to be merged into an existing BASIC program, with all line numbers starting at 7999. It relies on a companion machine code block (“mcdiscode”) loaded at address 62720 that handles the actual Z80 instruction decoding, with the BASIC layer providing a menu-driven interface for disassembly, hex/decimal conversion, memory peeking, and a simple address dictionary for labeling. The disassembler reads decoded instruction text from address 65216, uses PEEK 64973 to determine operand type, and dispatches to subroutines for relative jumps (including proper signed-offset calculation for JR and DJNZ) and address lookups. A custom hex-input function, defined via DEF FN, converts both digit and lowercase-letter hex characters to numeric values. The dictionary feature stores 8-byte records (2-byte address plus 6-character label) growing downward from address 63080, and a machine code search routine at 62720 performs label lookups during disassembly output. *** ## Program Analysis ### Program Structure The program is organized as a menu-driven toolkit with line numbers starting at 7999, explicitly designed to be merged with a user’s existing BASIC program. The entry point at line 7999 branches to 8183 (initialization), and the menu at line 8000 lists eight functional areas by their starting line numbers. | Line Range | Function | | --- | --- | | 7999–8029 | Entry point, initialization branch, menu display | | 8102 | Hex-digit conversion function (DEF FN) | | 8110–8128 | Hex loader (POKE bytes into memory) and address input subroutine | | 8130–8174 | Disassembler main loop | | 8180–8188 | Machine code loader and variable initialization | | 8190–8196 | Decimal-to-hex conversion subroutine | | 8210–8214 | Decimal-to-hex display utility | | 8220–8229 | Operand dispatcher (reads PEEK 64973 type flag) | | 8230–8252 | Dictionary entry (label assignment to an address) | | 8260–8288 | Dictionary lookup and display during disassembly | | 8290–8293 | Hex-to-decimal converter (interactive) | | 8300–8329 | Instruction display with color coding and JR offset resolution | | 8350–8385 | Memory peek utility with hex, decimal, and CHR$ display | | 8990–8996 | Save routine (BASIC + machine code block) | | 8999 | REM credits line | | 9000–9006 | Extended help text (intended for deletion after familiarization) | ### Machine Code Integration The core disassembly engine resides in a 2560-byte machine code block loaded at address 62720 via `LOAD "mcdiscode" CODE`. The BASIC program communicates with it through a shared memory interface: - `POKE 64044/64045/64046` — pass the address to disassemble (low and high bytes) - `POKE 64973,0` — clear the operand-type flag before calling the routine - `RANDOMIZE USR 65040` — invoke the disassembler; decoded mnemonic text is written to 65216 - `PEEK 64973` — read back operand type: 0 = none, 1 = byte/offset operand, 2 = word/address operand - `PEEK 64974` / `PEEK 64975` — retrieve the operand byte(s) - `PEEK 64045` / `PEEK 64046` — read back the updated program counter after disassembly A second machine code routine at 62720 (invoked via `USR 62720` after poking a target address into 62728/62734) performs the dictionary label search, returning either a pointer to a matching 6-character label or 0 if not found. ### Key BASIC Idioms and Techniques The hex-digit conversion at line 8102 uses `DEF FN v(v$)=CODE v$-48-39*(CODE v$>96)`. Subtracting 48 converts ASCII digits 0–9 to 0–9; the additional `-39*(CODE v$>96)` handles lowercase a–f, where `CODE "a"=97`, so `97-48-39=10` as required. This is a compact single-expression function avoiding any branching. The decimal-to-hex conversion subroutine at lines 8192–8196 builds the hex string right-to-left using repeated modulo-16 extraction and indexing into `h$="0123456789ABCDEF"`, terminating when the result is exactly 4 characters. The result is placed in `n$` for use by the caller. The address input subroutine (lines 8121–8128) supports toggling between decimal and hex entry: entering `"0"` flips the `h` flag with `LET h=NOT h` and loops back to re-prompt. Four-nibble hex input is parsed using four successive calls to `FN v()` with positional weights 4096, 256 (`tfs`), 16 (`sx`), and 1. ### Disassembly Loop The main disassembly loop (lines 8131–8174) operates as follows: 1. Call address input subroutine (GO SUB 8120) to get starting address `n` 2. Print address (decimal or hex depending on `h`) 3. Decompose `n` into high/low bytes and POKE into 64045/64046 4. Call machine code disassembler via `RANDOMIZE USR 65040` 5. Read mnemonic text from 65216 into `i$` by scanning for a zero terminator 6. Display mnemonic with optional INK 2 coloring for jump instructions 7. Dispatch operand display via GO SUB 8220 based on `PEEK 64973` 8. Advance `n` from updated PC in 64045/64046 and loop ### Jump Target Resolution Lines 8310–8329 handle relative jump operands for `JR` and `DJNZ` instructions. The signed offset is read from `PEEK 64974` and the resolved absolute address is computed as `n+2+oo-tfs*(oo>=128)`, where subtracting 256 when the offset byte is ≥ 128 correctly implements two’s-complement sign extension. The result is then displayed as either hex or decimal according to the `h` flag. ### Dictionary System The dictionary stores 8-byte records growing downward from `end=63080`. Each record consists of a 2-byte little-endian address followed by a 6-character label. Adding an entry (lines 8230–8252) prompts for an address and a label, POKEs the data, then decrements `end` by 8. The machine code search routine at 62720 receives the target address in 62728/62734 and scans the dictionary, returning either the address of the matching label string or 0 for no match. ### Memory Peek Utility Lines 8350–8385 implement a memory browser displaying each byte as a 4-digit hex address, 5-digit decimal value, 2-digit hex byte, decimal byte value, and the CHR$ character (suppressed for values below 32). The hex address display reuses the `n$`/8190 subroutine, and decimal values are right-justified to 5 characters using a string-padding loop at lines 8357–8357 (a `GO TO`-based loop prepending spaces until `LEN v$=5`). ### Variable Conventions | Variable | Purpose | | --- | --- | | `tfs` | 256 — used as the high-byte multiplier (“two fifty-six”) | | `sx` | 16 — used as the nibble multiplier (“sixteen”) | | `h` | Flag: 1=hex display/input mode, 0=decimal | | `h$` | “0123456789ABCDEF” — hex digit lookup string | | `n` | Current memory address being processed | | `n$` | Hex string result from conversion subroutine | | `end` | Current top of dictionary storage (grows downward from 63080) | | `d$` | Label string retrieved from dictionary | | `i$` | Decoded mnemonic string from disassembler output buffer | ### Notable Anomalies Line 8120 is the target of `GO SUB 8120` calls but does not appear in the listing — execution falls through to line 8121, which is the actual start of the address input subroutine. This is a standard technique where the GO SUB target line simply doesn’t exist and execution continues at the next available line. Line 8183 is the target of the initial `GO TO 8183` at line 7999, but it does not appear as an explicit line in the listing; line 8184 contains the variable initialization. After saving and reloading, execution resumes at 8184 via `SAVE "mcdis" LINE 8180`, which causes the loader to run first. The `CLEAR 62719` at line 8180 sets RAMTOP just below the machine code block, protecting it from BASIC memory management before the `LOAD "mcdiscode" CODE` at 8182 places the code at 62720. ## Source Code ``` 7999 GO TO 8183 8016 REM 8017 LET r=PEEK 23730+tfs*PEEK 23731 8018 CLS 8019 PRINT : PRINT 8020 PRINT "menu 8000","dis 8130","load 8110","dc-hx 8210","dict 8230","hx-dc 8290","peek 8350","save 8990" 8025 PRINT : PRINT "help 9000": PRINT : PRINT "FREE "; FREE ,"RAMTOP ";r: PRINT 8026 PRINT "(Enter ""GOTO ...."")": PRINT 8029 STOP 8102 DEF FN v(v$)=CODE v$-48-39*(CODE v$>96) 8110 GO SUB 8120 8111 PRINT : PRINT "input hex (lower case)" 8112 LET v$="" 8113 IF v$="" THEN INPUT v$ 8114 IF v$="s" THEN STOP 8116 POKE n,sx*FN v(v$(1))+FN v(v$(2)) 8117 LET n=n+1 8118 LET v$=v$(3 TO ) 8119 GO TO 8113 8121 PRINT "start addr" 8122 IF h THEN PRINT "(hex hi-lo)" 8123 IF NOT h THEN PRINT "(dec)" 8124 INPUT v$ 8125 IF v$="0" THEN LET h=NOT h: GO TO 8122 8126 IF NOT h THEN LET n=VAL v$ 8127 IF h THEN LET n=4096*FN v(v$(1))+tfs*FN v(v$(2))+sx*FN v(v$(3))+FN v(v$(4)) 8128 RETURN 8130 REM 8131 GO SUB 8120 8136 IF NOT h THEN PRINT n;" ";: GO TO 8140 8137 GO SUB 8190 8138 PRINT n$;" "; 8140 LET y=INT (n/tfs) 8142 LET x=n-tfs*y 8143 POKE 65216,0 8144 POKE 64045,x: POKE 64046,y 8145 POKE 64973,0 8148 RANDOMIZE USR 65040 8150 LET i=0: LET i$="" 8155 IF PEEK (65216+i)=0 THEN GO TO 8170 8158 LET i$=i$+CHR$ PEEK (65216+i) 8160 LET i=i+1: GO TO 8155 8165 PRINT i$; 8170 GO SUB 8300 8171 GO SUB 8220 8172 LET n=1+PEEK 64045+tfs*PEEK 64046 8174 GO TO 8136 8180 CLEAR 62719 8181 PRINT : PRINT "Continue loading..." 8182 LOAD "mcdiscode"CODE 8184 LET sx=16: LET tfs=256: LET h$="0123456789ABCDEF": LET h=1: LET d$="": LET end=63080 8188 CLS : GO TO 8000 8190 REM 8192 LET n$="": LET n1=n 8193 LET k1=INT (n1/sx) 8194 LET n$=h$(n1-sx*k1+1)+n$ 8195 IF LEN n$=4 THEN RETURN 8196 LET n1=k1: GO TO 8193 8210 PRINT "(dec)" 8211 INPUT n1 8212 PRINT n1;" "; 8213 LET n$="": GO SUB 8193 8214 PRINT n$: STOP 8220 IF PEEK 64973=0 THEN GO TO 8229 8222 PRINT TAB 19; 8224 IF PEEK 64973=1 THEN GO SUB 8310 8225 IF PEEK 64973=2 THEN GO SUB 8260 8229 PRINT : RETURN 8230 REM 8231 PRINT "enter addr" 8234 GO SUB 8122 8236 LET y=INT (n/tfs): LET x=n-tfs*y 8237 POKE end,x: POKE (end+1),y 8238 PRINT "label (6 chars)" 8240 INPUT l$ 8242 IF LEN l$<>6 THEN GO TO 8238 8244 FOR i=1 TO 6 8245 POKE (end+1+i),CODE l$(i) 8246 NEXT i 8250 LET end=end-8 8252 STOP 8260 LET x=PEEK 64974: LET y=PEEK 64975: LET n1=x+tfs*y 8261 POKE 62728,x: POKE 62734,y 8262 LET k=USR 62720 8263 IF k=0 THEN GO TO 8287 8264 LET d$="" 8265 FOR i=0 TO 5 8266 LET d$=d$+CHR$ PEEK (k+i) 8267 NEXT i 8268 PRINT d$;: RETURN 8287 IF h THEN LET n$="": GO SUB 8193: PRINT n$;: RETURN 8288 PRINT n1;: RETURN 8290 LET h=1: GO SUB 8122 8292 PRINT v$;" ";n 8293 STOP 8300 IF i$(1)="J" OR i$( TO 2)="DJ" OR i$( TO 2)="CA" THEN PRINT INK 2;i$;: RETURN 8305 PRINT i$;: RETURN 8310 LET oo=PEEK 64974 8315 IF i$( TO 2)<>"JR" AND i$( TO 2)<>"DJ" THEN PRINT oo;: RETURN 8320 LET n1=n+2+oo-tfs*(oo>=128) 8324 IF NOT h THEN PRINT n1;: RETURN 8328 LET n$="": GO SUB 8193 8329 PRINT n$;: RETURN 8350 GO SUB 8120 8351 GO TO 8381 8353 PRINT v$; 8355 LET v$=STR$ n 8357 IF LEN v$<5 THEN LET v$=" "+v$: GO TO 8357 8360 PRINT " ";v$;" "; 8362 LET t=PEEK n: LET s=INT (t/sx) 8364 PRINT h$(1+s);h$(1+t-sx*s);" "; 8365 PRINT t;TAB 18; 8367 IF t<32 THEN PRINT : GO TO 8380 8369 PRINT CHR$ t 8380 LET n=n+1 8381 GO SUB 8190 8385 LET v$=n$: GO TO 8353 8990 CLEAR 62719 8992 SAVE "mcdis" LINE 8180 8994 SAVE "mcdiscode"CODE 62720,2560 8996 GO TO 8183 8999 REM "mcdis" R Scoville 1984 9000 CLS : PRINT "You will still need paper and pencil to write machine code, but the routines in this programwill help. You can always GOTO 8000 for the menu. All hex num-bers must be entered with four digits (lower case only). To switch between hex and decimal entry, enter a single ""0""." 9001 PRINT "To write code, use ""load."" You may ENTER as often or as seldom as you wish. To stop entering, simply add ""s"" to the end of your code. You can then check for proper entry by using ""dis.""You can label any addresses you please by using ""dict.""" 9002 PRINT "The routines ""hex-dec"" and ""dec-hex"" simply translate from one to the other. The code for the program is 2560 bytes long (including the dictionary) and begins at 62720." 9003 PRINT "The routine ""peek"" will show theCHR$ values that are entered at each address. The program is purposely short and plain, so you may wish to ""save"" copies on all of your work tapes. Note that in the middle of the ""save,"" you will be prompted twice to press a key--once for the BASIC part and once for the machine code part." 9005 PRINT "These help notes (9000 to 9006) should be deleted after you become familiar with the pro- gram. Now GOTO 8000 and try it." 9006 PRINT '''''' ```