--- title: "ZX ToolBox" id: 56750 type: "computer_media" slug: "zx-toolbox" url: "http://localhost/computer_media/zx-toolbox/" markdown_url: "http://localhost/computer_media/zx-toolbox.md" published_at: "2024-09-22T23:31:59+00:00" modified_at: "2026-04-04T20:17:47+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/ZXToolBoxA.jpg" excerpt: "Three tape utilities in one package: display program names from tape headers, save and reload text records, and load a machine code toolkit via direct memory calls." 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: "Melbourne House Publishers" slug: "melbourne-house-publishers" taxonomy: "post_tag" url: "http://localhost/tag/melbourne-house-publishers/" - 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_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/ZX%20ToolBox%20%281983%29%28Melbourne%20House%20Publishers%29%28ZX81%29%28UK%29%28Cassette%29.zip" mediadate: "1983" producer_company: - id: 11087 title: "Melbourne House Publishers" type: "company" url: "http://localhost/company/melbourne-house-publishers/" images: - url: "http://localhost/wp-content/uploads/2024/09/ZXToolBoxA.jpg" - url: "http://localhost/wp-content/uploads/2024/09/ZXToolBoxB.jpg" related_products: - id: 14764 title: "ZX Toolbox" type: "product" url: "http://localhost/product/toolbox/" media_type_tags: "Utility" --- # ZX ToolBox ZX ToolBox is a collection of three utility programs for tape-based systems: a Directory utility, a Record utility, and a Toolkit loader. The Directory program uses RAND USR 16514 to invoke a machine code routine that reads tape header data, then inspects memory addresses 16444–16463 to extract and display program names, detecting the end of each name by checking for characters with values above 128 (high-bit set). The Record utility stores up to 96 characters of user-entered text by POKEing directly into memory starting at address 16714, and uses machine code calls at addresses 16523 and 16599 (USR 16523 for write, USR 16599 for read) to save and reload that data block. The Toolkit loader (line 5) calls RAND USR 16547 to initialize the machine code toolkit, with its routines encoded within REM statements at lines 20 and 30. *** ## Program Analysis ### Program Structure ZX ToolBox comprises three independent BASIC programs, each saved separately to tape. They are: 1. **Directory** (lines 10–200): Reads tape headers and displays up to 15 program names. 2. **Record** (lines 10–170): Provides a simple database record facility — read, write, or enter a 96-character record stored in RAM. 3. **Toolkit** (lines 5–30, appearing twice): Loads a machine code toolkit stored in REM statements. ### Directory Program The Directory utility loops 15 times (line 60, `FOR A=1 TO 15`). Each iteration POKEs 255 into address 16437 (line 50/160) to keep the system ready, then calls `RAND USR 16514` (line 70) to invoke a machine code routine that reads a tape block header into the system area. The program name is then extracted by inspecting bytes at addresses 16444–16463 (20 bytes, matching the ZX81 system variable area for tape header name storage). Name termination is detected by checking whether `PEEK B > 128` (line 100): on the ZX81, the last character of a stored name has its high bit set. When this terminating character is found, the program prints the character with 128 subtracted (line 140) and appends a closing quote, then pauses 400 frames before continuing to the next entry. ### Record Program The Record utility manages a fixed-length 96-character record stored at addresses 16714–16810 in RAM. The main menu (lines 30–50) offers three options: read back (option 1), write (option 2), or enter new data (option 3). Machine code entry points handle the tape operations: `RAND USR 16523` is called for write (option 2) and `RAND USR (16523 + 76)`, i.e. `RAND USR 16599`, for read (option 1). The expression `VAL "16523"+(A=VAL "1")*VAL "76"` on line 60 neatly selects the correct entry point using a Boolean multiply idiom — when `A=1`, the condition evaluates to `-1` (true), so `*76` yields `-76`, but as written the multiplication of `(A=VAL "1")*VAL "76"` gives `-76` when true; however since true is `-1` in BASIC, the actual computed address would be `16523 + (-1)*76 = 16447`. This suggests the intended behavior relies on the machine code layout, or the subtraction is intentional to select an earlier entry point for reading. Data entry (option 3, lines 80–140) clears all 96 record bytes to zero first, then POKEs in the ASCII codes of the entered string `A$` up to its length. `VAL "number"` is used consistently throughout for literal numeric arguments as a memory-saving technique. ### Toolkit Loader The Toolkit program is minimal: line 5 saves itself to tape, and line 10 executes `RAND USR 16547` to jump into machine code. Lines 20 and 30 are REM statements that contain the actual machine code data — a standard technique for embedding machine code in BASIC programs where the REM body is never executed as BASIC but is present in memory for the USR call to use. The program listing appears twice in the source, which is a duplication in the original listing (likely a typesetting artifact). ### Notable Techniques - **High-bit name termination**: Using `PEEK B > 128` to detect the last character of a tape header name is idiomatic for the ZX81 tape system, where the final byte of the name field has bit 7 set. - **Boolean arithmetic for branching**: In the Record program, `(A=VAL "1")*VAL "76"` selects between two machine code entry points without an explicit `IF` statement. - **VAL “number” idiom**: Numeric literals wrapped in `VAL` calls are used extensively in the Record program to reduce the size of the BASIC line stored in memory, since the tokenized number representation is replaced by a shorter string. - **Machine code in REM**: The Toolkit stores executable machine code inside REM statement bodies at lines 20 and 30, a well-established technique for attaching machine code routines to a BASIC program without needing a separate LOAD block. - **Direct memory POKEing for data storage**: Both the Directory and Record programs interact directly with system RAM rather than using BASIC arrays, giving precise control over data layout and enabling machine code routines to access the same locations. ### Memory Map Summary | Address | Usage | | --- | --- | | `16437` | POKEd to 255 to reset tape-read state between header reads | | `16444–16463` | Tape header name field (20 bytes); inspected for program name | | `16514` | Machine code entry: read tape header (Directory) | | `16523` | Machine code entry: write record to tape (Record) | | `16547` | Machine code entry: Toolkit initialization | | `16599` | Machine code entry: read record from tape (Record, = 16523+76) | | `16714–16809` | 96-byte record data storage area (Record) | ## Source Code ``` 10 REM 5[~~]RND FASTE[J]£ DIM )>#Y#<= RETURN3SQR **S PAUSE .#[N]4 LIST 5WRND2▒Y#<= RETURNPEEK COPY3SQR **K NEXT :[=]▞,$<= RETURN*ACS ##S PRINT ( PRINT 4▖ RETURN#K THENZACS -14**LN ABS RND/SGN 7 FOR 5#RNDR GOSUB # FOR SQR LPRINT E[G]£ DIM 20 PRINT AT 4,4;"+ DIRECTORY +" 30 PRINT 40 PAUSE 9999 50 POKE 16437,255 60 FOR A=1 TO 15 70 RAND USR 16514 80 PRINT "PROGRAM ";A;TAB 11;""""; 90 FOR B=16444 TO 16463 100 IF PEEK B>128 THEN GOTO 140 110 PRINT CHR$ PEEK B; 120 NEXT B 130 GOTO 150 140 PRINT CHR$ (PEEK B-128);"""" 150 PAUSE 400 160 POKE 16437,255 170 NEXT A 180 STOP 190 SAVE "DIRECTOR[Y]" 200 RUN 10 REM 5[~~]RND FASTE[J]£ DIM TAN LN ▙RND)ACS ▒( RETURN.#[N]4 RAND 5#INKEY$ )▒ SAVE ACS ▞[3] NEW▌LEN ▖#PEEK COPY▞A( RETURNY#<= RETURN▞7( RETURN$4 LOAD PI ( CLEAR- RETURN14 THEN3SQR LN COS RND/PEEK 7 FOR 5[F]INKEY$ R GOSUB # FOR SQR LPRINT E[G]£ DIM LN ▙RND)>#Y#<= RETURN3SQR **S PAUSE .#[N]4 LIST 5#INKEY$ 2▒Y#<= RETURNPEEK COPY3SQR **K NEXT :[=]▞,$<= RETURN*ACS ##S PRINT ( PRINT 4▖ RETURN#K THENZACS -14**LN COS RND/SGN 20 CLS 30 PRINT "PRESENT RECORD";AT VAL "4",VAL "0";"123456789012345678901234567890123456789012345678902234567890123456789012345678901234567890123456" 40 PRINT AT VAL "16",VAL "0";"1-READ, 2-WRITE, 3-ENTER" 50 INPUT A 60 IF A<=VAL "2" THEN RAND USR (VAL "16523"+(A=VAL "1")*VAL "76") 70 IF A<>VAL "3" THEN RUN 80 CLS 90 PRINT "ENTER YOUR NEW RECORD" 100 INPUT A$ 110 FOR A=VAL "1" TO VAL "96" 120 POKE VAL "16714"+A,VAL "0" 130 IF A<=LEN A$ THEN POKE VAL "16714"+A,CODE A$(A) 140 NEXT A 150 RUN 160 SAVE "RECOR[D]" 170 RUN 5 SAVE "TLKI[T]" 10 RAND USR (16547) 20 REM E▖RNDY#[W]K$LN E~~5#INKEY$ Q#5##/?[J]▞£VAL ▞4NOT ( CLEARAT ( RUN 5##6▖RNDE£RND)#▘; FOR 5)INKEY$ ▘. GOSUB [K]▞#LN CLEARRNDLN PLOT RND( SAVE LN SCROLL▝ GOSUB #▖RND5MINKEY$ ▘[(] GOSUB [K]# FAST▀VAL ▞ ( RETURNAT TAN E£RND:/7# RETURN#C▌LEN █#/ PRINT $4 PAUSE TAN TOOLKIT SUCCESSFULLY LOADED 30 REM 5 NEW#/$5 PLOT █/▒5LEN █/▀5Y█6[+]# GOSUB #£RND▀E(RND GOSUB PICOS ~~ RETURN#C POKE PLOT █▝/ LOAD 5 GOSUB #0RND[B] GOSUB PI GOSUB ###TAN 5#RND▘~~ )~~ # RETURN#COS #7#7 FOR ,, FOR STR$ #7#7;SGN / GOTO 5#RND7# RETURN#COS 77#7#7.#[M]C LIST # RETURN#4~~▘▌ ,, FOR GOSUB PI FOR / FOR RETURN REM 4 SCROLLQ PRINT / STOP:~~▞4Y NOT ( CLEARY#NOT $COS / PAUSE 5 SAVE "TLKI[T]" 10 RAND USR (16547) 20 REM E▖RNDY#[W]K$LN E~~5#INKEY$ Q#5##/?[J]▞£VAL ▞4NOT ( CLEARAT ( RUN 5##6▖RNDE£RND)#▘; FOR 5)INKEY$ ▘. GOSUB [K]▞#LN CLEARRNDLN PLOT RND( SAVE LN SCROLL▝ GOSUB #▖RND5MINKEY$ ▘[(] GOSUB [K]# FAST▀VAL ▞ ( RETURNAT TAN E£RND:/7# RETURN#C▌LEN █#/ PRINT $4 PAUSE TAN TOOLKIT SUCCESSFULLY LOADED 30 REM 5 NEW#/$5 PLOT █/▒5LEN █/▀5Y█6[+]# GOSUB #£RND▀E(RND GOSUB PICOS ~~ RETURN#C POKE PLOT █▝/ LOAD 5 GOSUB #0RND[B] GOSUB PI GOSUB ###TAN 5#RND▘~~ )~~ # RETURN#COS #7#7 FOR ,, FOR STR$ #7#7;SGN / GOTO 5#RND7# RETURN#COS 77#7#7.#[M]C LIST # RETURN#4~~▘▌ ,, FOR GOSUB PI FOR / FOR RETURN REM 4 SCROLLQ PRINT / STOP:~~▞4Y NOT ( CLEARY#NOT $COS / PAUSE ```