--- title: "Phone Book" id: 63154 type: "computer_media" slug: "phone-book" url: "http://localhost/computer_media/phone-book/" markdown_url: "http://localhost/computer_media/phone-book.md" published_at: "2026-01-30T17:36:44+00:00" modified_at: "2026-03-30T21:44:40+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/01/phone-book-1.png" excerpt: "A tape-based phone book that packs 250 contacts into a fixed-width string array and saves entry counts to a clever two-byte machine address on tape." 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/" indiv: - name: "Kristian Boisvert" slug: "boisvert-kristian" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-kristian/" genre: - name: "Home" slug: "home" taxonomy: "genre" url: "http://localhost/type/home/" media_contents: - id: 63045 title: "Byte Power September 1986" type: "computer_media" url: "http://localhost/computer_media/byte-power-september-1986/" media_type: "Program" programmers: - name: "Kristian Boisvert" slug: "boisvert-kristian" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-kristian/" mediadate: "1986" images: - url: "http://localhost/wp-content/uploads/2026/01/phone-book-1.png" - url: "http://localhost/wp-content/uploads/2026/01/phone-book-2.png" article_media: - id: 63034 title: "Phone Bok" type: "article" url: "http://localhost/article/phone-bok/" media_type_tags: "Home" --- # Phone Book This program is a telephone address book manager that stores up to 250 entries in a two-dimensional string array, each entry occupying 120 characters divided into fixed-width fields: name (25), address (33), city (16), state (26), zip code (7), and phone number (13). It provides a seven-option menu for entering, displaying, and editing records, plus tape LOAD/SAVE routines that persist both the string array and a two-byte metadata block (file number and entry count) using POKE/SAVE CODE at address 65534–65535. The search routine in the subroutine at line 5010 pads the query string with spaces to match the fixed 25-character name field before comparing. Line 1 uses PEEK 23681 (the FLAGS system variable area) as a self-boot guard, listing line 9999 and halting if the condition is met, while line 9999 itself saves and verifies the program with an auto-start LINE parameter. *** ## Program Structure The program is organised into clearly delineated sections by line-number ranges: | Line range | Purpose | | --- | --- | | 1–5 | Boot guard, splash screen, initialisation | | 10–96 | Array dimensioning and main menu loop | | 1000–1120 | Enter new record | | 2000–2210 | Display a record (calls shared display subroutine) | | 4000–4040 | Begin new file | | 5000–5140 | Search and change a record | | 6000–6070 | Load from tape | | 7000–7070 | Save to tape | | 8000–8530 | Error display and quit | | 9999 | Self-save/verify utility line | ## Data Storage Layout All contacts are held in a single array `a$(250,120)` declared at line 10. Each row is divided into fixed-width substring slices: - `A$(F, TO 25)` — Name (25 chars) - `A$(F,26 TO 58)` — Address (33 chars) - `A$(F,59 TO 74)` — City (16 chars) - `A$(F,75 TO 100)` — State (26 chars) - `A$(F,101 TO 107)` — Zip code (7 chars) - `A$(F,108 TO 120)` — Phone number (13 chars) The counter variable `NU` tracks how many entries have been added; `FILE` tracks the current file number for multi-file tape sessions. ## Boot Guard and Self-Save (Lines 1 and 9999) Line 1 peeks system variable address 23681. If this byte is zero the program executes `LIST 9999` then `STOP`, exposing the self-save line to the user as a prompt. Line 9999 performs `SAVE "PHONE BOOK" LINE 1` followed by `VERIFY "PHONE BOOK"`, saving the program with an auto-start at line 1. This line is intentionally never reached during normal execution. ## Tape Persistence and Metadata Block The save routine at line 7003 is particularly notable. Before saving the array it POKEs the current file number into address 65534 and the entry count (`NU`) into 65535 — the very top two bytes of the 64 KB address space. It then saves this two-byte region as a CODE block named `"#NAMESFILE"`. On load (line 6020), `LOAD "#NAMESFILE" CODE 65534` restores both values in one tape block, avoiding the need to store metadata inside the array itself. `POKE 23736,181` adjusts the tape header border colour byte before the CODE save. ## Search Routine and String Padding The search subroutine beginning at line 5010 uses a linear scan from `F=1` to `NU`. Because the name field is always stored as exactly 25 characters (padded by Spectrum string assignment), the query string `Q$` must also be padded before comparison. Lines 5023–5024 handle this: if `LEN Q$ < 25`, a `FOR` loop appends spaces one at a time until the lengths match. This is a simple but inefficient padding method; for short search strings it iterates up to 24 times per call. ## Menu and Input Idioms The main menu at line 70 uses a tight `INKEY$` polling loop without `PAUSE`, which will spin the CPU continuously. Option guards such as `IF Q$="2" AND NU<>0` (line 85) prevent display or edit operations when the file is empty. The entry routine uses `INPUT LINE` throughout, which accepts the full input line including spaces without the usual string-terminator stripping. ## Source Code ``` 1 CLS : IF PEEK 23681=0 THEN CLS : LIST 9999: STOP 3 PRINT AT 9,10;"PHONE BOOK";AT 11,3;"COPYRIGHT BYTE POWER 1986";AT 13,2;"WRITTEN BY KRISTIAN BOISVERT" 4 PAUSE 600 5 POKE 23658,8: LET FILE=1 10 DIM a$(250,120) 45 LET NU=0 50 CLS : PRINT AT 0,13;"MENU" 60 PRINT ''"1- ENTER NAME"''"2- DISPLAY A NAME ON SCREEN"''"3- CHANGE A NAME"''"4- LOAD NAMES FROM TAPE"''"5- SAVE NAMES ON TAPE"''"6- BEGIN A NEW FILE"''"7- QUIT PROGRAM" 70 LET Q$=INKEY$: IF Q$<"1" OR Q$>"7" THEN GO TO 70 75 IF Q$="6" THEN GO TO 4000 80 IF Q$="1" THEN GO TO 1000 85 IF Q$="2" AND NU<>0 THEN GO TO 2000 86 IF Q$="3" AND NU<>0 THEN GO TO 5000 87 IF Q$="4" THEN GO TO 6000 90 IF Q$="5" AND NU<>0 THEN GO TO 7000 95 IF Q$="7" THEN GO TO 8500 96 GO TO 70 1000 LET NU=NU+1: CLS : PRINT AT 0,10;"ENTER NAME" 1001 IF NU=251 THEN LET NU=250: PRINT '"WARNING: FILE FULL!"''"SAVE THIS FILE AND BEGIN A NEW ONE?": INPUT LINE T$: IF T$="Y" THEN GO SUB 7000: LET FILE=FILE+1: LET NU=0: GO TO 50 1005 LET F=NU 1010 INPUT "NAME:"; LINE A$(NU,1 TO 25): IF A$(NU,1 TO 25)=" " THEN LET NU=NU-1: GO TO 50 1020 INPUT "ADDRESS:"; LINE A$(NU,26 TO 58) 1030 INPUT "CITY:"; LINE A$(NU,59 TO 74) 1040 INPUT "STATE:"; LINE A$(NU,75 TO 100) 1050 INPUT "ZIP CODE:"; LINE A$(NU,101 TO 107) 1060 INPUT "PHONE NUMBER:"; LINE A$(NU,108 TO 120) 1090 GO SUB 2090 1100 INPUT "IS THIS CORRECT? (Y/N) "; LINE Q$ 1110 IF Q$="N" THEN GO TO 1001 1115 INPUT "ANOTHER? (Y/N) "; LINE Q$ 1116 IF Q$="N" THEN GO TO 50 1120 GO TO 1000 2000 CLS : PRINT AT 0,8;"DISPLAY A NAME": GO SUB 5010 2005 IF Q=1 THEN GO TO 50 2080 CLS : PRINT AT 0,8;"DISPLAY A NAME" 2081 GO SUB 2090: PAUSE 0: GO TO 50 2090 CLS : PRINT "FILE:";FILE;" #";F: PRINT AT 2,0;"NAME:";A$(F, TO 25) 2100 PRINT AT 4,0;"ADD.:";A$(F,26 TO 58) 2110 PRINT AT 6,0;"CITY:";A$(F,59 TO 74) 2120 PRINT AT 8,0;"STATE:";A$(F,75 TO 100) 2140 PRINT AT 10,0;"ZIP:";A$(F,101 TO 107) 2150 PRINT AT 12,0;"PHONE:";A$(F,108 TO 120) 2210 RETURN 4000 CLS : PRINT "ARE YOU SURE?" 4010 LET Q$=INKEY$ 4020 IF Q$="Y" THEN LET FILE=FILE+1: GO TO 10 4030 IF Q$<>"N" THEN GO TO 4010 4040 GO TO 50 5000 CLS : PRINT AT 0,9;"CHANGE A NAME": GO SUB 5010: IF Q=0 THEN GO TO 5080 5005 IF Q=1 THEN GO TO 50 5010 LET Q=0: INPUT "NAME:"; LINE Q$ 5020 IF Q$="" THEN LET Q=1: RETURN 5021 PRINT AT 2,0;"SEARCHING ";Q$;" " 5022 FOR F=1 TO NU 5023 IF LEN Q$<>25 THEN FOR G=1 TO 25-LEN Q$: LET Q$=Q$+" ": NEXT G 5024 IF Q$=A$(F, TO 25) THEN RETURN 5025 NEXT F 5026 PRINT "NAME NOT IN CURRENT FILE" 5060 PAUSE 600: CLS : GO TO 5000 5080 GO SUB 2090: INPUT "NEW NAME:"; LINE Q$: IF Q$="" THEN GO TO 5082 5081 LET A$(F, TO 25)=Q$ 5082 INPUT "NEW ADDRESS:"; LINE Q$: IF Q$="" THEN GO TO 5084 5083 LET A$(F,26 TO 58)=Q$ 5084 INPUT "NEW CITY:"; LINE Q$: IF Q$="" THEN GO TO 5086 5085 LET A$(F,59 TO 74)=Q$ 5086 INPUT "NEW STATE:"; LINE Q$: IF Q$="" THEN GO TO 5088 5087 LET A$(F,75 TO 100)=Q$ 5088 INPUT "NEW ZIP CODE:"; LINE Q$: IF Q$="" THEN GO TO 5090 5089 LET A$(F,101 TO 107)=Q$ 5090 INPUT "NEW PHONE NUMBER:"; LINE Q$: IF Q$="" THEN GO TO 5092 5091 LET A$(F,108 TO 120)=Q$ 5095 GO SUB 2090 5100 INPUT "IS THIS CORRECT? (Y/N) "; LINE Q$ 5110 IF Q$="N" THEN GO TO 5080 5120 INPUT "ANOTHER? (Y/N) "; LINE Q$ 5130 IF Q$="N" THEN GO TO 50 5140 GO TO 5000 6000 REM LOAD 6003 CLS : INPUT "LOAD FILE#";FI 6010 LOAD "FILE#"+STR$ FILE DATA A$() 6020 LOAD "#NAMESFILE"CODE 65534 6070 GO TO 50 7000 REM SAVE 7001 CLS 7002 PRINT "SAVE FILE#";FILE 7003 POKE 65534,FILE: POKE 65535,NU: SAVE "FILE#"+STR$ FILE DATA A$(): POKE 23736,181: SAVE "#NAMESFILE"CODE 65534,2 7010 INPUT "VERIFY? "; LINE Q$ 7020 IF Q$="Y" THEN VERIFY "FILE#"+STR$ FILE DATA A$(): VERIFY "#NAMESFILE"CODE 65534 7070 GO TO 50 8000 CLS : PRINT AT 10,5;"ERROR- COMMAND ABORTED" 8010 PAUSE 0: GO TO 50 8500 CLS : PRINT AT 10,0;"ARE YOU SURE?" 8510 LET Q$=INKEY$ 8515 IF Q$="Y" THEN NEW 8520 IF Q$="N" THEN GO TO 50 8530 GO TO 8510 9999 SAVE "PHONE BOOK" LINE 1: VERIFY "PHONE BOOK" ```