--- title: "MCUtil" id: 71212 type: "computer_media" slug: "mcutil" url: "http://localhost/computer_media/mcutil/" markdown_url: "http://localhost/computer_media/mcutil.md" published_at: "2026-08-30T22:57:48+00:00" modified_at: "2026-08-30T22:57:48+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/mcutil.png" alt: "MCUtil screen" excerpt: "A seven-function machine code toolkit lets you browse, load, relocate, and convert memory contents, topped off with a self-modifying BASIC line renumberer." 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/" genre: - name: "Machine Language" slug: "machine-language" taxonomy: "genre" url: "http://localhost/type/machine-language/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/MCUtil%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/mcutil.png" alt: "MCUtil screen" media_type_tags: "Machine Language" --- # MCUtil MCUtil is a machine code utility suite providing seven tools for inspecting, entering, relocating, and converting machine code in memory. The program displays memory contents as a navigable block grid (up to 160 bytes) with joystick support via STICK, uses UDG characters (defined by POKEing addresses 65368–65391) to render a three-character cursor overlay with OVER 1, and performs hex conversion by hand using CHR$ arithmetic rather than a built-in hex function. A separate decimal-to-machine-code address splitter computes the high and low bytes of any 16-bit address, and a relocation routine handles both upward and downward block moves with careful loop-bound calculations to avoid overwriting source data. Lines 9958–9999 contain a self-modifying BASIC renumberer that patches GO TO/GO SUB target line numbers in both their display text and the hidden floating-point number stored inline by the Spectrum’s tokenizer. ### Program Structure The program is organized as a menu dispatcher at line 2560 that routes to seven functional modules. Lines below 2560 contain the modules; lines above 2560 through 3700 hold shared subroutines; and lines 9958–9999 are a standalone BASIC renumberer appended at the top of the line-number space to avoid interfering with renumbering itself. | Lines | Module | | --- | --- | | 1050–1400 | Review MC in blocks (navigable grid) | | 1410–1530 | Decimal address → Low/High byte splitter | | 1540–1780 | Dec code pair → decimal value converter | | 1790–2000 | Machine code loader (POKE loop) | | 2040–2470 | Relocate machine code block | | 2480–2550 | RAMTOP display | | 2560–2850 | Main menu | | 2860–2950 | Hex conversion subroutine | | 2970–3280 | Input & calculate (N / S / E) | | 3350–3380 | RAMTOP PEEK subroutine | | 3410–3450 | Memory array builder (DIM M$) | | 3490–3560 | UDG cursor definition | | 3570–3700 | List MC by address with hex column | | 9958–9999 | Self-modifying BASIC renumberer | ### Memory Array and Block Display The block viewer (lines 1050–1400) stores up to 160 bytes as a string array `M$` dimensioned to `(N,4)` at line 3410, where each element holds the decimal string representation of a POKEd byte (e.g., `"255"`). The display arranges bytes in a 20-column × 8-row grid; the cursor position `(X,Y)` maps to a byte index via `SA = 8*X + INT(Y/4) + 1`, reflecting the four-character-wide fields. Three UDG characters `\a\b\c` (chars 144–146) are defined at lines 3520–3550 by POKEing 24 bytes starting at address 65368. They form a three-part cursor bracket drawn with `PRINT AT X,Y; OVER 1;"\a\b\c"`, using OVER 1 (XOR mode) so the cursor can be erased by redrawing it without restoring the background explicitly. ### Joystick and Keyboard Navigation Lines 1360–1390 implement dual-input navigation combining STICK (port 2 joystick) with cursor keys 5/6/7/8. The STICK return value is decoded by checking ranges: values 1/5/9 = up, 2/6/10 = down, ST>7 = right, ST>3 AND ST<7 = left. Boundary clamping uses Boolean arithmetic rather than IF statements; for example, `X = X + (X<0) - (...overflow condition...)` keeps X within the valid grid row range, with separate logic for the final partial row controlled by `XLIM` and `YLIM`. ### Hex Conversion Subroutine Lines 2890–2950 convert a decimal byte value stored in `M$(I)` to a two-character hex string in `H$`. It splits the value into high and low nibbles, then converts each nibble to its ASCII character: digits 0–9 via `CHR$(nibble+48)` and letters A–F via `CHR$(nibble+55)`. There is no built-in hex function, so this manual approach is the standard workaround. ### Relocation Routine The relocator (lines 2040–2470) handles both upward and downward moves. If the new address `NS` is below the source start `S`, it copies forward (low-to-high addresses, lines 2270–2300) to avoid overwriting unread source bytes; if `NS > S`, it copies backward (high-to-low, lines 2350–2380). After copying, the vacated region is zeroed. The zero-fill loop bound is computed with a conditional expression: - If the gap is smaller than the block: zero only the gap (`S-NS-1` or `NS-S-1` iterations). - If the gap equals or exceeds the block: zero the entire original block (`E-S` iterations). This avoids zeroing bytes that were never part of the original block when the destination is far from the source. ### Input and Calculation Subroutine Lines 2970–3280 accept any two of three values — number of bytes `N`, starting address `S`, and ending address `E` — and derive the third. The logic at lines 3110–3270 handles all combinations. A guard at line 3110 detects the degenerate case where both `N$` and `S$` are empty and redirects to the error handler at 3280. The subroutine shares a `Q` variable with the menu to enforce the 160-byte limit only for the block viewer (option 1). ### RAMTOP Subroutine Lines 3350–3380 read RAMTOP from Spectrum system variables at addresses 23730 (low byte) and 23731 (high byte), combining them as `RT = 256*RH + RL`. The relocation routine at lines 3460–3480 (reachable but not called from the menu) would lower RAMTOP by POKEing the updated value back to the same addresses. ### Self-Modifying BASIC Renumberer (Lines 9958–9999) This module walks the BASIC program area using the PROG system variable at address 23635. It uses two inline functions: `FN A(X)` reads a little-endian 16-bit word (low byte first) and `FN B(S)` reads a big-endian 16-bit word (high byte first), used to read line lengths and line numbers respectively. The first pass (9962–9975) scans all lines below 9958 for GO TO (token 236) and GO SUB (token 237) statements, collecting the memory address and current target line number of each into the string `T$` in 9-character records: 5-character address + 4-character line number. The second pass (9976–9987) walks the program again, renumbering lines starting at 1000 in steps of 10 by POKEing the new line number into the two header bytes of each line. For each line, it checks `T$` for any GO TO/GO SUB targeting that line and calls the subroutine at 9990 if found. The patch subroutine (9990–9999) updates both the display text of the target number (4 bytes via `CODE(STR$ BASE)(J)`) and the Spectrum’s hidden inline floating-point representation (5 bytes starting 6 bytes after the token), reconstructing the binary float from the new line number using `LN` to determine the exponent byte. ### Bugs and Anomalies - Line 3230 recalculates `N = E-S+1` and then checks `IF E"M" THEN GO TO 1330 1290 INPUT "ENTER NEW BYTE: ";Q$ 1300 IF Q$="" OR VAL Q$<0 OR VAL Q$>255 OR VAL Q$ <> INT VAL Q$ THEN GO TO 1330 1310 POKE (S+SA-1), VAL Q$:LET M$(SA)=Q$ 1320 PRINT AT X,Y;M$(SA) 1330 LET ST=STICK (1,2) 1350 LET X1=X:LET Y1=Y 1360 LET X=X+(ST=2 OR ST=6 OR ST=10 OR INKEY$="6")-(ST=1 OR ST=5 OR ST=9 OR INKEY$="7") 1370 LET X=X+(X<0)-((X>XLIM AND Y <=YLIM) OR (X>XLIM-1 AND Y>YLIM)) 1380 LET Y=Y+4*((ST>7 OR INKEY$="8")-((ST>3 AND ST<7) OR INKEY$="5")) 1390 LET Y=Y+4*((Y<0)-((Y>28 AND X<(XLIM)) OR (Y>YLIM AND X=XLIM))) 1400 GO TO 1220 1410 REM *********************** 1420 REM DECIMAL TO MC 1430 REM *********************** 1440 CLS 1450 PRINT "\{20}\{1}DECIMAL TO MC\{20}\{0}" 1460 PRINT 1470 INPUT "ENTER ADDRESS (DECIMAL): ";Q$ 1480 LET H= INT ((VAL Q$)/256) 1490 LET L= VAL Q$-256*H 1500 PRINT "ADDRESS: ";Q$; TAB 16;"L: ";L; TAB 24;"H: ";H 1510 INPUT "NEW ADDR?"'"HIT ENTER TO RET TO MENU ";Q$ 1520 IF Q$="" THEN GO TO 2560 1530 GO TO 1480 1540 REM *********************** 1550 REM DEC CODE TO DEC VALUE 1560 REM *********************** 1570 CLS 1580 PRINT 1590 PRINT AT 0,0;"\{20}\{1}DEC. CODE TO DEC. CONVERSION\{20}\{0}" 1600 LPRINT AT 0,0;"\{20}\{1}DEC. CODE TO DEC. CONVERSION\{20}\{0}" 1610 PRINT 1620 LPRINT 1630 PRINT AT 3,0;"LOW"; AT 3,5;"HIGH"; AT 3,12;"ADDR OR VALUE" 1640 LPRINT AT 3,0;"LOW"; AT 3,5;"HIGH"; AT 3,12;"ADDR OR VALUE" 1650 PRINT 1660 LPRINT 1670 INPUT "HIT ENTER TO RETURN TO MENU..."'"DECIMAL CODE: LOW ";L$ 1680 IF L$="" THEN GO TO 2560 1690 PRINT L$; 1700 LPRINT L$ 1710 INPUT "DECIMAL CODE: HIGH ";H$ 1720 IF H$="" THEN GO TO 2560 1730 PRINT TAB 5;H$; 1740 LPRINT TAB 5;H$ 1750 LET A=256*(VAL H$)+ VAL L$ 1760 PRINT TAB 12;A 1770 LPRINT TAB 12;A 1780 GO TO 1670 1790 REM *********************** 1800 REM MC LOADER 1810 REM *********************** 1820 CLS 1830 PRINT "\{20}\{1}MC LOADER\{20}\{0}" 1840 GO SUB 2970 1850 CLS 1860 PRINT "INPUT CODE (DEC) FOR EA ADDR" 1870 LET SUM=0 1880 FOR I=1 TO N 1890 PRINT INK 3; VAL (S$)+I-1; 1900 INPUT C$ 1910 IF C$="" THEN CLS :GO TO 2560 1920 IF VAL C$>255 THEN GO TO 1900 1930 PRINT TAB (10- LEN C$);C$ 1940 POKE (VAL (S$)+I-1), VAL C$ 1950 LET SUM=SUM+ VAL C$ 1960 NEXT I 1970 PRINT 1980 PRINT "CHECKSUM= ";SUM 1990 PRINT "HIT ANY KEY TO RETURN TO MENU":PAUSE 4E4 2000 GO TO 2560 2010 REM *********************** 2020 REM RELOCATE MC 2030 REM *********************** 2040 CLS 2050 PRINT "RELOCATE MACHINE CODE" 2060 PRINT 2070 GO SUB 2970 2080 PRINT "CURRENT STARTING ADDRESS OF"'"BLOCK TO BE RELOCATED IS ";S$ 2090 PRINT 2100 LPRINT "CURRENT STARTING ADDRESS OF"'"BLOCK TO BE RELOCATED IS ";S$ 2110 LPRINT 2120 PRINT "ENDING ADDRESS OF BLOCK"'"TO BE RELOCATED ";E 2130 LPRINT "ENDING ADDRESS OF BLOCK"'"TO BE RELOCATED ";E 2140 PRINT 2150 LPRINT 2160 INPUT "ENTER NEW START ADDRESS ";T$ 2170 IF T$="" THEN GO TO 2560 2180 LET NS= VAL T$ 2190 IF NS<1 OR NS <> INT NS THEN GO TO 2160 2200 PRINT "NEW STARTING ADDESS IS ";NS 2210 LPRINT "NEW STARTING ADDESS IS ";NS 2220 PRINT 2230 LPRINT 2240 PRINT "BLOCK BEING RELOCATED" 2250 PRINT 2260 IF S(E-S))) 2320 POKE (E-I),0 2330 NEXT I 2340 GO TO 2450 2350 FOR I=0 TO E-S 2360 LET C= PEEK (E-I) 2370 POKE (E+NS-S-I),C 2380 NEXT I 2390 PRINT "NOW ZEROING VACATED MEMORY LOCATIONS" 2400 FOR I=0 TO ((NS-S-1)*((NS-S) <=(E-S)))+((E-S)*((NS-S)>(E-S))) 2410 POKE (S+I),0 2420 IF I/200= INT (I/200) THEN PRINT "STILL RUNNING" 2430 NEXT I 2440 PRINT 2450 PRINT "THE BLOCK OF MACHINE CODE HAS"'"BEEN RELOCATED TO START AT"'NS 2460 PAUSE 600 2470 GO TO 2560 2480 REM *********************** 2490 REM RAMTOP 2500 REM *********************** 2510 CLS 2520 GO SUB 3350 2530 PRINT "RAMTOP IS AT ";RT 2540 PAUSE 600 2550 GO TO 2560 2560 REM *********************** 2570 REM MENU 2580 REM *********************** 2590 CLS 2600 PRINT TAB 10;"\{20}\{1}\ \ \ \m\e\n\u\ \ \ \{20}\{0}" 2610 PRINT 2620 PRINT 2630 PRINT "1...REVIEW MC IN BLOCKS" 2640 PRINT 2650 PRINT "2...REVIEW MC WITH ADDRESSES" 2660 PRINT 2670 PRINT "3...CONVERT DECIMAL TO MC" 2680 PRINT 2690 PRINT "4...CONVERT DEC CODE TO DEC VAL" 2700 PRINT 2710 PRINT "5...MACH CODE LOADER" 2720 PRINT 2730 PRINT "6...RELOCATE MACH CODE" 2740 PRINT 2750 PRINT "7...RAMTOP LOCATION" 2760 PRINT 2770 INPUT "ENTER SELECTION ";Q 2780 CLS :IF Q=1 THEN PRINT "REVIEW MC IN BLOCKS":PRINT :PRINT "JOYSTICK IS ALLOWED IN PORT 2":GO TO 1050 2790 IF Q=2 THEN GO TO 3570 2800 IF Q=3 THEN PRINT "CONVERT DECIMAL TO MC":PRINT :GO TO 1410 2810 IF Q=4 THEN PRINT "CONVERT DEC CODE TO DEC VALUE":PRINT :GO TO 1540 2820 IF Q=5 THEN PRINT "MACHINE CODE LOADER":PRINT :GO TO 1790 2830 IF Q=6 THEN GO TO 2040 2840 IF Q=7 THEN GO TO 2480 2850 GO TO 2560 2860 REM ****************** 2870 REM HEX CALCULATION 2880 REM ****************** 2890 LET P= INT (VAL M$(I)/16) 2900 LET Q= VAL M$(I)-16*P 2910 IF P <=9 THEN LET H$= CHR$ (P+48) 2920 IF P>9 THEN LET H$= CHR$ (P+55) 2930 IF Q <=9 THEN LET H$=H$+ CHR$ (Q+48) 2940 IF Q>9 THEN LET H$=H$+ CHR$ (Q+55) 2950 RETURN 2970 REM *********************** 2980 REM INPUT AND CALC ROUTINE 2990 REM *********************** 3000 IF Q=1 THEN PRINT AT 5,0;"MAXIMUM OF 160 BYTES" 3010 PRINT AT 10,0;"ENTER ANY 2 OF 3 ITEMS" 3020 PRINT AT 12,0;"NUMBER OF BYTES:" 3030 PRINT AT 14,0;"STARTING ADDRESS:" 3040 PRINT AT 16,0;"ENDING ADDRESS:" 3050 PRINT AT 18,5;"HIT ENTER IF UNKNOWN" 3060 INPUT "ENTER NUMBER OF BYTES ";N$:IF N$="" THEN GO TO 3100 3070 LET N= VAL N$:IF N<1 OR N <> INT N THEN GO TO 3060 3080 IF Q=1 AND N>160 THEN GO TO 3060 3090 PRINT AT 12,17;N 3100 INPUT "ENTER STARTING ADDRESS ";S$ 3110 IF N$="" AND S$="" THEN GO TO 3280 3120 IF S$="" THEN GO TO 3160 3130 LET S= VAL S$:IF S<1 OR S <> INT S THEN GO TO 3100 3140 PRINT AT 14,18;S 3150 IF N$ <>"" THEN LET E=S+N-1:PRINT AT 16,16;E:PRINT AT 18,0;"CREATING MEMORY ARRAY ":PAUSE 180:RETURN 3160 INPUT "ENTER ENDING ADDRESS ";E$:IF E$="" THEN GO TO 3280 3170 LET E= VAL E$ 3180 IF E<1 OR E <> INT E THEN GO TO 3250 3190 IF S$="" THEN LET S=E-N+1 3200 IF E START ADDR":PAUSE 180:PRINT AT 20,0," ": GO TO 3160 3210 PRINT AT 16,16;E 3220 PRINT AT 14,18;S 3230 LET N=E-S+1:IF E160 THEN PRINT FLASH 1; AT 5,0;"MAXIMUM OF 160 BYTES":PAUSE 180:PRINT AT 14,18;" "; AT 16,16;" ":GO TO 3000 3250 PRINT AT 12,17;N 3260 PRINT AT 12,17;N 3270 PRINT AT 18,0;"CREATING MEMORY ARRAY ":PAUSE 180:RETURN 3280 PRINT FLASH 1; AT 10,0;"ENTER ANY 2 OF 3 ITEMS":PAUSE 180 3290 PRINT AT 12,17;" "; AT 14,18;" "; AT 16,16;" ":GO TO 3010 3300 GO SUB 3350 3310 IF S$="" THEN LET S$="RT+1" 3320 PRINT "STARTING ADDRESS IS "; VAL S$ 3330 PRINT 3340 RETURN 3350 LET RL= PEEK 23730 3360 LET RH= PEEK 23731 3370 LET RT=256*RH+RL 3380 RETURN 3390 LET S$="RT+1-N" 3400 RETURN 3410 DIM M$(N,4) 3420 FOR I=1 TO N 3430 LET M$(I)= STR$ PEEK (S-1+I) 3440 NEXT I 3450 RETURN 3460 GO SUB 3350 3470 PRINT "RAMTOP BEING LOWERED TO ";RT-N:POKE 23730,(RT-N)-256* INT ((RT-N)/256):POKE 23731, INT ((RT-N)/256) 3480 RETURN 3490 REM *********************** 3500 REM CREATE CURSOR 3510 REM *********************** 3515 RESTORE 3520 FOR I=65368 TO 65391 3530 READ D:POKE I,D 3540 NEXT I 3550 DATA 255,128,128,128,128,128,128,255,255,0,0,0,0,0,0,255,255,1,1,1,1,1,1,255 3560 RETURN 3570 REM *********************** 3580 REM LIST BY ADDRESS 3590 REM *********************** 3600 CLS :PRINT "\{20}\{1}LIST MC BY ADDRESS\{20}\{0}" 3610 GO SUB 2970 3615 GO SUB 3410 3620 CLS :PRINT "ADDR"; TAB 7;"DEC"; TAB 13;"HEX":PRINT 3630 FOR I=1 TO N 3640 PRINT S-1+I; TAB 7;M$(I); 3650 GO SUB 2860 3660 PRINT TAB 13;H$ 3670 LPRINT S-1+I; TAB 7;M$(I); TAB 13;H$ 3680 NEXT I 3690 PRINT AT 21,0;"HIT ANY KEY TO RETURN TO MENU":PAUSE 4E4 3700 GO TO 2560 9958 CLS :PRINT "RENUMBERING STARTING AT 1000" 9959 LET T$="":LET X=23635 9960 DEF FN A(X)= PEEK X+256* PEEK (X+1) 9961 DEF FN B(S)=256* PEEK S+ PEEK (S+1) 9962 LET S= FN A(X) 9963 LET LINE= FN B(S):IF LINE >=9958 THEN GO TO 9976 9964 LET LENGTH= FN A(S+2) 9965 IF PEEK (S+4)=234 THEN GO TO 9969 9966 FOR I=S+4 TO S+LENGTH+2 9967 IF PEEK I=236 OR PEEK I=237 THEN GO SUB 9971 9968 NEXT I 9969 LET S=S+LENGTH+4 9970 GO TO 9963 9971 IF PEEK (I+5)=14 THEN GO TO 9974 9972 PRINT "N0N-STANDARD COMMAND, LINE ";LINE 9973 STOP 9974 LET T$=T$+ STR$ I+ CHR$ PEEK (I+1)+ CHR$ PEEK (I+2)+ CHR$ PEEK (I+3)+ CHR$ PEEK (I+4) 9975 RETURN 9976 LET BASE=1000:LET X=23635 9977 LET S= FN A(X) 9978 LET LINE= FN B(S):IF LINE >=9958 THEN BEEP 5,0:STOP 9979 LET LENGTH= FN A(S+2) 9980 FOR I=1 TO LEN T$ STEP 9 9981 IF VAL T$(I+5 TO I+8)=LINE THEN GO SUB 9990 9982 NEXT I 9983 POKE S, INT (BASE/256) 9984 POKE S+1,BASE-256* INT (BASE/256) 9985 LET BASE=BASE+10 9986 LET S=S+LENGTH+4 9987 GO TO 9978 9990 FOR J=1 TO 4 9991 POKE (VAL T$(I TO I+4)+J), CODE (STR$ BASE)(J) 9992 NEXT J 9993 LET BYTE1=128+ INT (LN BASE/ LN 2+1) 9994 LET BYTE2=BASE*65536/(2^(BYTE1-128)) 9995 LET MEMORY= VAL T$(I TO I+4) 9996 POKE MEMORY+6,BYTE1 9997 POKE MEMORY+7, INT (BYTE2/256)-128 9998 POKE MEMORY+8,BYTE2-256* INT (BYTE2/256) 9999 RETURN ```