--- title: "Programming Utilities" id: 57774 type: "computer_media" slug: "programming-utilities" url: "http://localhost/computer_media/programming-utilities/" markdown_url: "http://localhost/computer_media/programming-utilities.md" published_at: "2024-10-26T17:12:13+00:00" modified_at: "2026-04-03T07:58:07+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/4_ProUtl.png" excerpt: "A machine code helper that converts hex, decimal, and two-byte values using clever ZX81 system variable tricks — designed to drop straight into any project." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 57780 title: "CassFile2" type: "computer_media" url: "http://localhost/computer_media/cassfile2-2/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/CassFile2%20%28198x%29%28Various%29%28TS1000%29%28US%29%28Program%29.zip/CassFile2%20%28198x%29%28Various%29%28TS1000%29%28US%29%28Program%29%2FB%20Side%2F4_ProUtl.tzx" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/4_ProUtl.png" media_type_tags: "Utility" --- # Programming Utilities This ZX81/TS1000 BASIC utility provides three number-base conversion tools for machine code programmers: hexadecimal-to-decimal, decimal-to-hexadecimal, and a two-byte (LSB/MSB) splitter. The hex-to-decimal converter manually parses each character of an input string using ZX81 character codes (digits map to codes 28–37, letters A–F to 38–43), then multiplies each nibble by the appropriate power of 16. The two-byte converter exploits the ZX81’s RAND statement, which stores its argument in the SEED system variable at addresses 16434–16435, to extract the least-significant and most-significant bytes of any 16-bit value without machine code. The program is explicitly designed to be renumbered and embedded into other programs using the [Delphic Toolkit](http://localhost/product/4k-toolkit-2/). *** ## Program Structure The program begins at line 9000, consistent with its stated purpose of being appended to existing programs and renumbered. It is organised into clearly delineated subroutines and sections: 1. **Main menu** (9035–9115): Displays five options and branches on numeric input. 2. **Hex-to-Dec converter** (9120–9295): String parsing loop with character-code arithmetic. 3. **Dec-to-Hex converter** (9305–9450): Successive division by powers of 16 with a lookup string. 4. **2-Byte converter** (9480–9645): Uses `RAND` and `PEEK` to split a 16-bit value. 5. **Shared subroutines**: Border-drawing (9650–9695), scroll-clear (9455–9475), field-clear (9630–9645), and error display (9700–9720). 6. **Save and exit** (9725–9730). ### Hex-to-Decimal Converter The converter at lines 9215–9275 manually parses a hex string entered as `H$`. It iterates from the most-significant to least-significant character, converting each to a numeric nibble via ZX81 character codes: | Character type | Code range | Operation | Result range | | --- | --- | --- | --- | | Digits 0–9 | 28–37 | `C = C - 28` | 0–9 | | Letters A–F | 38–43 | `C = C - 28` (yields 10–15) | 10–15 | There is a subtle bug at line 9255: after the first `IF C>=28 AND C<=37` subtracts 28, the condition at line 9260 checks `IF C>=38 AND C<=43`, which can never be true after subtraction. However, line 9255 also has an erroneous `GOTO 9270` guard (line 9255 does not branch, unlike 9260). In practice, letters A–F (codes 38–43) fall through correctly: the first `IF` at 9250 does not fire (38>37), so control reaches 9260 where subtraction of 28 yields 10–15. The logic accidentally works despite the redundant second condition check on line 9255. ### Decimal-to-Hex Converter This section (9350–9445) uses the string `A$="0123456789ABCDEF"` as a nibble lookup table. The 16-bit decimal value `Q` is decomposed by successive integer division: - `D = INT(Q/4096)` — most-significant nibble - `E = INT(R/256)` — second nibble - `H = INT(R/16)` — third nibble - `L = R - 16*H` — least-significant nibble Each nibble indexes into `A$` with `A$(D+1 TO D+1)`, exploiting ZX81 BASIC’s one-based string slicing. Input of `123456` is used as a magic number to switch to the hex-to-dec converter (line 9385), and `0` returns to the main menu. There is a bug at line 9395: `GOTO 400` (without the 9000-block prefix) is almost certainly a typographical error for `GOTO 9400`. In context the intent is to re-prompt after a bounds error, but line 400 does not exist, which would cause an error at runtime for out-of-range values. ### Two-Byte Converter The most elegant technique in the program appears at lines 9610–9620. Rather than implementing bit-shifting arithmetic, the converter exploits the ZX81 system variable *SEED* (used by `RAND`) stored at addresses 16434 (LSB) and 16435 (MSB): - `RAND N` — stores `N` as a 16-bit integer into SEED. - `PEEK 16434` — reads the least-significant byte. - `PEEK 16435` — reads the most-significant byte. This avoids any division or masking arithmetic and produces correct results for any value 1–65535. Zero is treated as an abort condition (line 9575) and redirects to the main menu. ### Display and UI Techniques The border-drawing subroutine at 9650–9695 uses `PLOT` to draw a rectangular frame: vertical sides by iterating Y from 0 to 43, and three horizontal lines (top, a mid-divider at Y=32, and bottom at Y=0). The `% %` sequences in `PRINT` statements produce flashing/inverse text effects by using ZX81 character alternation — here they animate the prompt text by printing the string twice in a short `FOR` loop (lines 9170–9205, 9525–9560), toggling between the percent-decorated version and a plain version to simulate flashing without `FLASH`. The scroll-clearing subroutine at 9455–9475 clears result lines by printing spaces over rows 10–18, then resets the row counter `A` to 10. This prevents results from scrolling off the decorated border. ### Notable Anomalies - Line 9395 references `GOTO 400` rather than `GOTO 9400` — a clear typo that would cause a runtime error for out-of-range decimal inputs. - The `FAST`/`SLOW` pairs around the border subroutine calls (e.g. lines 9140/9150, 9500/9510) draw the border quickly in FAST mode then return to SLOW for user interaction — a standard ZX81 speed optimisation idiom. ## Source Code ``` 9000 REM ** ROCKETTER BBS ** 9005 REM ** 714-630-2488 ** 9010 REM 8/1/N 300/1200 BAUD 9015 REM % % % % % % %O%K% %T%O% %R%U%N% % % % % % 9020 REM PROGRAM IS DESIGNED TO AID IN CONVERTING NUMBERS FOR MACHINE CODE APPLICATIONS 9025 REM MAY BE ADDED TO ANY CURRENT PROGRAM AND RENUMBERED 9030 REM WITH THE DELPHIC TOOLKIT WITHOUT ANY CHANGE 9035 FAST 9040 CLS 9045 GOSUB 9650 9050 LET Y$="TIMEX 1000" 9055 PRINT AT 2,11;Y$ 9060 PRINT AT 3,7;"PROGRAMING UTILITES" 9065 PRINT AT 8,3;"1) HEX TO DEC." 9070 PRINT AT 10,3;"2) DEC TO HEX." 9075 PRINT AT 12,3;"3) 2 BYTE CONVERTER." 9080 PRINT AT 14,3;"4) SAVE PROGRAM." 9085 PRINT AT 16,3;"5) STOP." 9090 PRINT AT 20,6;"% %E%N%T%E%R% %Y%O%U%R% %C%H%O%I%C%E% " 9095 INPUT B 9100 IF B=2 THEN GOTO 9315 9105 IF B=3 THEN GOTO 9480 9110 IF B=4 THEN GOTO 9725 9115 IF B=5 THEN STOP 9120 REM **** HEX TO DEC ***** 9125 REM *********************** 9130 CLS 9135 LET A=8 9140 FAST 9145 GOSUB 9650 9150 SLOW 9155 PRINT AT 2,11;Y$ 9160 PRINT AT 3,3;"** HEX TO DEC CONVERTER **" 9165 PRINT AT 7,10;"""0"" ABORTS";AT 8,6;"""T"" FOR DEC TO HEX" 9170 FOR F=1 TO 2 9175 PRINT AT 20,1;"% %E%N%T%E%R% %H%E%X% %N%U%M%B%E%R% " 9180 FOR J=1 TO 20 9185 NEXT J 9190 PRINT AT 20,1;" ENTER HEX NUMBER " 9195 FOR J=1 TO 20 9200 NEXT J 9205 NEXT F 9210 LET T=0 9215 INPUT H$ 9220 LET D=1 9225 IF H$="0" THEN GOTO 9035 9230 IF H$="T" THEN GOTO 9325 9235 FOR P=LEN (H$)-1 TO 0 STEP -1 9240 LET C=CODE (H$(D TO D)) 9245 LET D=D+1 9250 IF C>=28 AND C<=37 THEN LET C=C-28 9255 IF C>=28 AND C<=37 THEN GOTO 9270 9260 IF C>=38 AND C<=43 THEN LET C=C-28 9265 GOTO 9270 9270 LET T=T+C*16**P 9275 NEXT P 9280 LET A=A+2 9285 IF A>18 THEN GOSUB 9455 9290 PRINT AT A,4;"HEX: ";H$;" = ";T;" DEC" 9295 GOTO 9170 9300 REM *********************** 9305 REM **** DEC TO HEX ***** 9310 REM *********************** 9315 CLS 9320 GOSUB 9650 9325 GOSUB 9455 9330 PRINT AT 3,3;"** DEC TO HEX CONVERTER **" 9335 PRINT AT 8,5;"123456 FOR HEX TO DEC" 9340 LET A=8 9345 LET A=A+2 9350 LET A$="0123456789ABCDEF" 9355 PRINT AT 20,1;"% %E%N%T%E%R% %D%E%C% %N%U%M%B%E%R% " 9360 FOR F=1 TO 20 9365 NEXT F 9370 PRINT AT 20,1;" ENTER DEC NUMBER " 9375 INPUT Q 9380 IF Q=0 THEN GOTO 9035 9385 IF Q=123456 THEN GOTO 9130 9390 IF Q>65535 OR Q<0 THEN PRINT "TRY AGAIN" 9395 IF Q>65535 OR Q<0 THEN GOTO 400 9400 LET D=INT (Q/4096) 9405 LET R=Q-4096*D 9410 LET E=INT (R/256) 9415 LET R=R-256*E 9420 LET H=INT (R/16) 9425 LET L=R-16*H 9430 IF A>18 THEN GOSUB 9455 9435 PRINT AT A,4;"DEC: ";Q;" = "; 9440 IF A>18 THEN GOSUB 9455 9445 PRINT A$(D+1 TO D+1);A$(E+1 TO E+1);A$(H+1 TO H+1);A$(L+1 TO L+1);" HEX" 9450 GOTO 9345 9455 FOR F=2 TO 10 STEP 2 9460 PRINT AT 8+F,4;" " 9465 NEXT F 9470 LET A=10 9475 RETURN 9480 REM *********************** 9485 REM ** 2 BYTE CONVERTER ** 9490 REM *********************** 9495 CLS 9500 FAST 9505 GOSUB 9650 9510 SLOW 9515 PRINT AT 2,10;Y$ 9520 PRINT AT 3,4;"** 2 BYTE CONVERTER **" 9525 FOR F=1 TO 2 9530 FOR J=0 TO 10 9535 NEXT J 9540 PRINT AT 20,1;"% %E%N%T%E%R% %N%U%M%B%E%R% ";AT 20,20;"""0"" ABORTS" 9545 FOR J=0 TO 10 9550 NEXT J 9555 PRINT AT 20,1;" ENTER NUMBER " 9560 NEXT F 9565 INPUT N 9570 GOSUB 9630 9575 IF N=0 THEN GOTO 9035 9580 IF N>65535 OR N<1 THEN GOTO 9700 9585 PRINT AT 8,12;N 9590 REM % %P%L%A%C%E%S% %N%U%M%B%E%R% %I%N%T%O% 9595 REM % %S%Y%S%T%E%M% %V%A%R%I%A%B%L%E% %"%S%E%E%D%" 9600 REM % %T%H%E%N% %H%A%S% %C%O%M%P%U%T%E%R% % % % % 9605 REM % %O%U%T%P%U%T% %C%O%N%T%E%N%T%S% % 9610 RAND N 9615 PRINT AT 10,8;"LEAST BYTE= ";PEEK 16434 9620 PRINT AT 12,8;"MOST BYTE= ";PEEK 16435 9625 GOTO 9525 9630 PRINT AT 8,12;" " 9635 PRINT AT 10,19;" " 9640 PRINT AT 12,19;" " 9645 RETURN 9650 FOR F=0 TO 43 9655 PLOT 0,F 9660 PLOT 63,F 9665 NEXT F 9670 FOR F=0 TO 63 9675 PLOT F,43 9680 PLOT F,32 9685 PLOT F,0 9690 NEXT F 9695 RETURN 9700 PRINT AT 20,1;"% %N%U%M%B%E%R% %T%O% %L%A%R%G%E% %O%R% %T%O% %S%M%A%L%L% % " 9705 FOR F=0 TO 100 9710 NEXT F 9715 PRINT AT 20,1;" " 9720 GOTO 9525 9725 SAVE "2 BYT%E" 9730 GOTO 9035 ```