Magazine File is a database management program for cataloging magazine articles, storing up to 500 records in a string array where each 32-character record packs a 4-character magazine name, 7-character subject, 13-character article title, and 5-character date. The program offers a menu-driven interface with eight options: loading and saving data via tape or disk, adding new entries, correcting existing records with a cursor-based character editor, searching by subject or by magazine name and date, and printing a sorted listing. The sort routine at line 7015–7080 implements a bubble sort over the entire array before printing. Several bugs are present, including a logic error in the input validation at line 700 (using OR instead of AND), off-by-one field slicing in the subject search at line 5010 (requesting 6 characters instead of 7), and an unused commented-out GO SUB dispatch at line 215.
Program Analysis
Program Structure
The program is organized into clearly separated functional blocks, each at a round line-number boundary that corresponds to a menu selection. Navigation is achieved by the dispatch at line 220: GO TO s*1000, routing selections 1–8 directly to lines 1000–8000. A shared tape/disk prompt subroutine lives at lines 500–520, and a printer-size prompt subroutine (partially broken) occupies lines 700–720, though neither is actually called in the final code.
| Line range | Function |
|---|---|
| 1–30 | Initialization: splash screen, blank-padding string, array declaration |
| 100–220 | Main menu display and input dispatch |
| 500–520 | Unused tape/disk selection subroutine |
| 700–720 | Unused printer-size subroutine (contains logic bug) |
| 1000–1040 | Load data from tape/disk |
| 2000–2250 | New entry input and storage |
| 3000–3050 | Save and verify data |
| 4000–4350 | Record correction with character-level editor |
| 5000–5240 | Search by subject |
| 6000–6240 | Search by magazine name and date |
| 7000–7320 | Bubble sort and print listing |
| 8000–8050 | Exit, with unsaved-data warning |
| 9900–9998 | REM note and SAVE header |
Data Layout
All records are stored in the two-dimensional string array w$(500,32), declared at line 30. Each row is exactly 32 characters, packed as follows:
- Characters 1–4: Magazine name (4 letters)
- Characters 5–11: Subject (7 letters) — though the entry routine pads to 8 with
e$( TO 8) - Characters 12–24: Article name (13 letters) — padded to 14 with
n$( TO 14) - Characters 25–29: Date (5 characters, format xx/xx) — padded to 5 with
d$( TO 5)
The fields as entered total 4+8+14+5 = 31 characters, leaving one character unused or causing the date to overflow beyond column 29 into 30. This is a latent layout inconsistency. The variable n tracks the number of active records; on load, lines 1020–1023 scan for the first blank entry to determine n.
The blank-padding string b$ at line 20 is 128 spaces, used throughout to right-pad inputs before slicing to the required field width — a standard Sinclair BASIC string-truncation idiom.
Character-Level Record Editor
The correction routine (lines 4026–4110) implements a simple cursor editor over a 32-character string. It loops l from 0 to 31, displaying the record and flashing a block cursor at position l using FLASH 1 and OVER 1. On each iteration it calls PAUSE 0 followed by INKEY$ to capture a keypress without blocking indefinitely. The cursor can move left with CHR$ 8 (the cursor-left key) by decrementing l by 2 (the loop NEXT l adds 1 back). Printable characters (CODE 32–143) overwrite the character at m$(l+1). Pressing ENTER (CHR$ 13) commits the edit by jumping to line 4300.
Search Routines
Both search routines (lines 5000 and 6000) use the same nested-loop pattern: an outer loop over all n records with index j, and an inner loop over each character of the search key with index f, branching out on the first mismatch. A match falls through to a PRINT and then rejoins the outer loop’s NEXT to continue searching, allowing all matching records to be displayed.
The subject search at line 5030 slices w$(j)(6 TO 12) — a 7-character window — and compares it character-by-character against t$. However, line 5010 pads and slices t$ to 6 characters (t$( TO 6)), then the inner loop runs f=1 TO 7, meaning the 7th comparison always reads t$(7) which is out of the declared slice. This is a bug that will cause an error in practice.
Bubble Sort
Lines 7015–7080 implement a standard bubble sort over all n records. The outer loop runs q=1 TO n-1 and the inner loop r=1 TO n-q. Adjacent records are compared as full 32-character strings (lexicographic order by magazine name first), and swapped if out of order. This sorts the in-memory array in place before printing, but permanently reorders w$() for the rest of the session.
Bugs and Anomalies
- Line 700 — printer validation logic: The condition
l$<>"Y" OR l$<>"y" OR l$<>"N" OR l$<>"n"is always true (a string cannot simultaneously equal “Y” and “y”), so the INPUT loop never exits. The correct operator isAND. This subroutine is also never called. - Line 215 — commented-out dispatch: The REM at line 215 contains a computed GO SUB formula (
25*(s*s-4*s+63)/3) that would route options 1, 3, and 7 through the tape and printer subroutines. This was commented out and replaced with the simpler direct GO TO at line 220, leaving the subroutines unreachable. - Line 2010 — field width off-by-one: The prompt says “4 letters” but
e$( TO 5)retains 5 characters. The subject prompt says “7 letters” buts$( TO 8)retains 8. These inconsistencies shift the expected field positions. - Line 5010 — subject search truncation:
t$( TO 6)produces a 6-character key, but the inner comparison loop runs to 7, accessingt$(7)which is beyond the slice and will cause a Subscript out of range error. - Line 4026 — FOR loop missing colon:
FOR l=0 TO 31: PRINT AT 10,0;m$;AT 15,0;b$places the PRINT on the same line as the FOR, which is valid syntax but means the display refresh only happens on each iteration’s start, not after a keypress — the cursor flash may not render correctly. - Line 7100 —
POKE 23692,255: This pokes the system variable SCRCT (scroll counter) to prevent the “scroll?” prompt during the print loop, a common Spectrum BASIC technique for uninterrupted output.
Notable Techniques
- Use of
b$(a long blank string) concatenated before slicing to guarantee fixed-width field padding without error-checking the input length. GO TO s*1000as a compact computed dispatch replacing a chain of IF statements.PAUSE 0/INKEY$idiom for single-keypress capture in the character editor.POKE 23692,255to suppress the scroll prompt during bulk list printing.- The REM at line 9900 preserves a TS2068 MOVE command (for DOS file copy) as documentation of the intended disk deployment path.
Content
Source Code
1 REM Junk; needs debugging start to finish
5 CLS : FLASH 1: BORDER 6: PAPER 6: INK 0: PRINT AT 10,10;"Magazine File";AT 11,8;"by Alan Friedman": PAUSE 120: FLASH 0
7 REM from CTM 5/86
10 LET n=0
20 LET b$=" "
30 DIM w$(500,32)
100 CLS : PRINT " MAGAZINE FILE"
110 LET l$=" ": PRINT ''l$;"Menu"'l$;"1 Read data from tape/disc"'l$;"2 New Entry"'l$;"3 Save data to tape/disc"'
120 PRINT l$;"4 Correction"'l$;"5 Search by subject"'l$;"6 Search by Mag./Date"'l$;"7 Print List"'l$;"8 Exit"'
200 PRINT l$; FLASH 1;"Choose one"
210 INPUT s: FLASH 0: IF s<1 OR s>8 OR s<>INT s THEN GO TO 210
215 REM IF s=1 OR s=3 OR s=7 THEN CLS : GO SUB 25*(s*s-4*s+63)/3
220 GO TO s*1000
500 INPUT "Using Tape(T) or Disc(D)?";y$
510 IF y$="T" OR y$="t" OR y$="D" OR y$="d" THEN GO TO 520
515 GO TO 500
520 RETURN
700 INPUT "Want full size printer (Y/N)";l$: IF l$<>"Y" OR l$<>"y" OR l$<>"N" OR l$<>"n" THEN GO TO 700
720 RETURN
1000 CLS : PRINT AT 12,0;"Start the Tape and"; FLASH 1';"Press enter": INPUT y$
1010 LOAD "mag data" DATA W$()
1020 FOR n=1 TO 500: IF w$(n,1 TO 4)=" " THEN GO TO 1023
1022 NEXT n
1023 LET n=n-1
1040 CLS : GO TO 100
2000 CLS
2010 PRINT AT 10,0;"Enter magazine name (4 letters)": INPUT e$: LET e$=e$+b$: LET e$=e$( TO 5)
2020 PRINT AT 0,0;e$;AT 10,0;b$
2030 PRINT AT 10,0;"Enter subject (7 letters)": INPUT s$: LET s$=s$+b$: LET s$=s$( TO 8)
2050 PRINT AT 1,0;s$;AT 10,0;b$
2060 PRINT AT 10,0;"Enter article name (13 letters)": INPUT n$: LET n$=n$+b$: LET n$=n$( TO 14)
2080 PRINT AT 2,0;n$;AT 10,0;b$
2090 PRINT AT 10,0;"Enter magazine date (xx/xx)": INPUT d$: LET d$=d$+b$: LET d$=d$( TO 5)
2110 PRINT AT 3,0;d$;AT 10,0;b$
2120 PAUSE 120: CLS : PRINT AT 10,5;e$;AT 11,5;s$;AT 12,5;n$;AT 13,5;d$
2125 INPUT "Is this OK? Y/N";a$: CLS : IF a$="n" OR a$="N" THEN GO TO 2000
2200 LET n=n+1
2210 LET w$(n)=e$+s$+n$+d$
2220 INPUT "Another entry? Y/N";a$: IF a$="y" OR a$="Y" THEN GO TO 2000
2250 CLS : GO TO 100
3000 CLS
3010 SAVE "mag data" DATA w$(): PRINT "rewind to verify": VERIFY "mag data" DATA w$()
3050 CLS : GO TO 100
4000 CLS
4010 PRINT AT 10,0;"Enter the number of the listing to be corrected": INPUT v
4020 CLS : PRINT AT 10,0;w$(v): LET m$=w$(v)
4025 INPUT "Is this OK? Y/N";y$: IF y$="n" OR y$="N" THEN GO TO 4000
4026 FOR l=0 TO 31: PRINT AT 10,0;m$;AT 15,0;b$
4050 PRINT AT 10,l; FLASH 1; OVER 1;CHR$ 32
4060 PAUSE 0: LET i$=INKEY$
4070 IF i$=CHR$ 8 AND l THEN LET l=l-2
4100 IF CODE i$>31 AND CODE i$<144 THEN LET m$(l+1)=i$
4105 IF CODE i$=13 THEN GO TO 4300
4110 NEXT l
4300 LET w$(v)=m$: CLS : PRINT AT 10,0;"New entry # ";v;"is"'w$(v)
4320 INPUT "Another change? Y/N";a$: IF a$="y" OR a$="Y" THEN GO TO 4000
4350 CLS : GO TO 100
5000 CLS
5010 INPUT "Enter Subject";t$: LET t$=t$+b$: LET t$=t$( TO 6): PRINT AT 0,10;t$
5030 FOR j=1 TO n: LET i$=w$(j)(6 TO 12)
5060 FOR f=1 TO 7: IF t$(f)<>i$(f) THEN GO TO 5200
5080 NEXT f
5090 GO TO 5300
5200 NEXT j
5240 INPUT "Press enter to return to menu";a$: CLS : GO TO 100
5300 PRINT w$(j): GO TO 5200
6000 CLS
6010 PRINT AT 10,0;"Enter magazine name (4 letters)": INPUT a$: LET a$=a$+b$: LET a$=a$( TO 4)
6020 PRINT AT 10,0;b$;AT 10,0;"Enter magazine date (xx/xx)": INPUT k$: LET k$=k$+b$: LET k$=k$( TO 5)
6025 LET a$=a$+k$
6030 CLS : FOR j=1 TO n: LET i$=w$(j)(1 TO 4)+w$(j)(28 TO 32)
6050 FOR f=1 TO 9: IF a$(f)<>i$(f) THEN GO TO 6200
6080 NEXT f: GO TO 6300
6200 NEXT j
6240 INPUT "Press any key to return to menu";z$: CLS : GO TO 100
6300 PRINT w$(j): GO TO 6200
7000 CLS
7010 INPUT "Want a hard copy? Y/N";y$: CLS
7015 FOR q=1 TO n-1: FOR r=1 TO n-q
7020 LET h$=w$(r): LET i$=w$(r+1)
7030 IF h$<=i$ THEN GO TO 7080
7050 LET w$(r)=i$: LET w$(r+1)=h$
7080 NEXT r: NEXT q
7100 FOR a=1 TO n: POKE 23692,255
7200 IF y$="y" OR y$="Y" THEN LPRINT 'w$(a)
7300 PRINT a'w$(a)
7310 NEXT a
7320 INPUT "Press any key to return to menu";v$: CLS : GO TO 100
8000 CLS
8010 FLASH 1: PRINT AT 12,0;"Magazine file end"'"Has data been saved?": INPUT y$: IF y$="y" OR y$="Y" THEN GO TO 8050
8020 FLASH 0: GO TO 3000
8050 FLASH 0: CLS : STOP
9899 STOP
9900 REM OUT 244,1: MOVE "Mag File.bas",5: OUT 244,0
9997 STOP
9998 SAVE "Mag File" LINE 5
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
