--- title: "Saver" id: 55727 type: "computer_media" slug: "saver" url: "http://localhost/computer_media/saver/" markdown_url: "http://localhost/computer_media/saver.md" published_at: "2024-07-05T00:18:47+00:00" modified_at: "2026-03-30T21:45:16+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240704-torp.png" excerpt: "Load any tape header and instantly get a ready-to-use SAVE command back — this utility decodes file type, name, length, and start address automatically." 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: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 55711 title: "ISTUG Public Domain Library 8" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-8/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Saver%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240704-torp.png" - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240704-toqi.png" media_type_tags: "Utility" --- This program reads a tape header block using a machine code routine loaded at address 32768 and displays the metadata — file type, name, length, and start address — decoded from the header bytes stored at addresses 33280–33294. It reconstructs a ready-to-type SAVE command string, including the correct keyword (LINE, CODE, DATA, or DATA$) based on the file type byte at 33280. A lookup table at line 230 maps the eight possible header type codes (0–3 covering BASIC, numeric array, string array, and code) to their display names and BASIC SAVE keywords. The program also handles the special case of a SCREEN$ file by detecting address 16384 and length 6912. Users can optionally send the SAVE command to a printer via LPRINT, reload another header, or re-save the utility itself. *** ## Program Analysis ### Program Structure The program divides into four logical phases: 1. **Initialization (lines 10–30):** Loads the companion machine code block “Saver code” to address 32768, then zeros two bytes at 33293–33294. 2. **Header capture (lines 40–70):** Prompts the user to play a tape, calls the machine code at `USR 32768` to read a tape header, then beeps to indicate completion. 3. **Data decoding (lines 80–170):** Parses the raw header bytes from 33280–33294, looks up the file type, trims the filename, and reconstructs the SAVE command string. 4. **Output and menu (lines 180–240):** Displays options to print via LPRINT, load another header, or re-save the utility; line 240 performs the re-save. ### Header Memory Map The machine code routine deposits the decoded header fields into a fixed block of RAM starting at 33280. The layout used by the BASIC decoding code is: | Address | Content | Length | | --- | --- | --- | | 33280 | File type byte (0–3) | 1 | | 33281–33290 | Filename (10 chars, space-padded) | 10 | | 33291–33292 | Parameter 1 (length / LINE number) | 2 | | 33293–33294 | Parameter 2 (start address / array letter) | 2 | This layout mirrors the standard Spectrum tape header block structure, offset by 33280 from address 0. Parameter 1 (`L`) is computed as `(PEEK 33292)*256+PEEK 33291` (little-endian) and Parameter 2 (`S`) similarly from 33293–33294. ### File Type Lookup Table Line 230 holds a `DATA` statement with four pairs of strings: a display name and a SAVE keyword fragment for each of the four header types (0 = BASIC, 1 = numeric array, 2 = string array, 3 = machine code). Line 80 reads through `Q` iterations (0 to `T`, where `T` is the type byte) to land on the correct pair, storing them in `T$` and `D$`. ### Filename Trimming Lines 100–110 scan the 10-character filename string `n$` from right to left, stopping at the last non-space character. The resulting index `c` is then used as a TO-slice bound — `N$( TO c)` — throughout the output lines, neatly stripping trailing spaces without any string manipulation function. ### SAVE Command Reconstruction Lines 120–150 build the extra parameter string `E$` depending on file type. For BASIC (type 0), `s$` is set to `" (Line #)"` and `E$` to the autostart line number. For code (type 3), `E$` becomes `start,length`. The special case at line 150 detects a SCREEN$ save (start 16384, length 6912) and substitutes the keyword `SCREEN$`, clearing `D$` so the output reads `SAVE "name" SCREEN$` correctly. ### Machine Code Component The 472-byte machine code block is loaded at address 32768 (just below the BASIC program area set by `CLEAR 32000`) and called via `RANDOMIZE USR 32768` at line 60. The routine is responsible for the low-level tape reading that the Spectrum ROM’s own LOAD mechanism would ordinarily handle; using machine code allows the header bytes to be deposited into a known RAM location without affecting the BASIC program. The `CLEAR 32000` at line 10 ensures the machine code area is protected from BASIC memory management. ### User Interface Loop Lines 180–220 implement a simple three-option menu using `PAUSE 0` followed by `INKEY$` checks. This is a standard keypress-detection idiom: `PAUSE 0` waits until any key is pressed, then the subsequent `IF INKEY$=` tests read the still-held key. If none of the three keys match, line 220 loops back to line 190 to re-test. ### Notable Anomalies - Line 30 pre-zeros 33293–33294, which correspond to the start-address parameter for code files. This prevents a stale value being displayed if the machine code routine does not overwrite them (e.g., for BASIC file headers where this field is unused). - The variable `s` (lowercase) at line 150 refers to the same value as `S` computed at line 130; Spectrum BASIC is case-insensitive for variable names of length > 1, but single-letter variables are case-sensitive — however `S` and `s` are *the same* single-letter variable in Spectrum BASIC (keywords are tokenized; variable names are stored in lowercase internally). This works correctly. - The `DATA` at line 230 contains only four type entries (types 0–3), which matches the complete set of valid Spectrum header types, so no out-of-range read can occur under normal tape operation. ## Source Code ``` 10 CLEAR 32000: LOAD "Saver code"CODE : BEEP .2,22 20 CLS : PRINT TAB 10;" S A V E R ";AT 4,9;"THIS PROGRAM";AT 6,6;"DISPLAYS OR PRINTS";AT 8,4;"TAPE HEADER INFORMATION" 30 POKE 33293,0: POKE 33294,0 40 PRINT FLASH 1;AT 18,3;" PRESS ENTER AND START TAPE "; FLASH 0;AT 20,11;"WHEN READY" 50 PAUSE 0: PRINT AT 18,1;" ";" NOW LOADING HEADER ";AT 20,8;"STOP TAPE AT BEEP" 60 RANDOMIZE USR 32768 70 BEEP .8,22: CLS 80 LET T=PEEK 33280: RESTORE 230: FOR Q=0 TO T: READ T$: READ D$: NEXT Q 90 LET n$="": FOR q=33281 TO 33290: LET n$=n$+CHR$ (PEEK q): NEXT q 100 FOR q=10 TO 1 STEP -1: IF n$(q)=" " THEN NEXT q 110 LET c=q 120 LET s$=" (Address)": IF T=0 THEN LET s$=" (Line #)" 130 LET L=(PEEK 33292)*256+PEEK 33291: LET S=(PEEK 33294)*256+PEEK 33293 140 LET E$="": IF T=0 OR T=3 THEN LET E$=STR$ S: IF T=3 THEN LET E$=E$+","+STR$ L 150 IF s=16384 AND l=6912 THEN LET E$="SCREEN$": LET D$="" 160 PRINT AT 0,10;" S A V E R ";AT 2,0;"TYPE OF FILE: ";t$;AT 4,0;"NAME OF FILE: ";n$( TO c);AT 6,0;"LENGTH OF FILE:";L;" Bytes";AT 8,0;"START OF FILE: ";S;s$ 170 PRINT AT 12,0;"TO SAVE THIS FILE USE:";AT 14,0;"SAVE """;N$( TO c);""" ";D$;" ";E$ 180 PRINT AT 18,0;"PRESS 'P' TO PRINT ABOVE DATA","PRESS 'H' TO LOAD ANOTHER HEADERPRESS 'S' TO SAVE THIS PROGRAM" 190 PAUSE 0: IF INKEY$="p" THEN LPRINT "SAVE """;N$( TO c);""" ";D$;" ";E$: LPRINT : LPRINT 200 IF INKEY$="h" THEN CLS : GO TO 20 210 IF INKEY$="s" THEN CLS : GO TO 240 220 GO TO 190 230 DATA "Basic Program","LINE","Numeric Array","DATA ()","String Array","DATA $ ()","Machine Code","CODE" 240 SAVE "SAVER" LINE 10: SAVE "Saver code"CODE 32768,472 ```