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 routineRANDOMIZE USR 65040— invoke the disassembler; decoded mnemonic text is written to 65216PEEK 64973— read back operand type: 0 = none, 1 = byte/offset operand, 2 = word/address operandPEEK 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:
- Call address input subroutine (GO SUB 8120) to get starting address
n - Print address (decimal or hex depending on
h) - Decompose
ninto high/low bytes and POKE into 64045/64046 - Call machine code disassembler via
RANDOMIZE USR 65040 - Read mnemonic text from 65216 into
i$by scanning for a zero terminator - Display mnemonic with optional INK 2 coloring for jump instructions
- Dispatch operand display via GO SUB 8220 based on
PEEK 64973 - Advance
nfrom 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.
Content
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 ''''''
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


