Stamp Collector is a philatelic inventory management program that allows users to track postage stamp collections by Scott catalog number and condition category. It uses a two-dimensional string array, `F$`, dimensioned at 600 rows by 15 characters, where each record packs a 5-character Scott number, a 1-character condition code, a 6-character quantity field, and a 1-character want-list flag. The program supports 18 stamp categories (singles, blocks, plate blocks, coils, imperforate, first day covers, and strips in mint or used conditions) encoded as alphanumeric codes 1–9 and A–H. Options include adjusting inventory quantities with positive or negative values, searching records by number and type, listing wanted stamps, and saving the entire array to tape.
Program Analysis
Program Structure
The program is organized as a menu-driven application anchored at line 70, which calls the main menu subroutine at 1000 and dispatches to one of five branches based on the value of O$. The structure is as follows:
- Lines 50–80: Initialization — sets SLOW mode, dimensions the master array, and enters the menu loop.
- Lines 1000–1099: Main menu display and input subroutine.
- Lines 1500–1599: Code-to-description translation subroutine (maps
C$to human-readableJ$). - Lines 2000–2022: Quit confirmation handler.
- Lines 2500–2999: Option 1 — Adjust inventory (add/remove a stamp record or update quantity).
- Lines 3000–3320: Option 2 — Search inventory by Scott number and/or category.
- Lines 3500–3710: Option 3 — List all stamps flagged on the want list.
- Lines 4000–4020: Option 4 — Save the inventory array to tape.
- Lines 5000–5999: Classification input subroutine (displays category table, reads code into
C$).
Data Storage Layout
All data is held in the two-dimensional string array F$(600,15). Each row encodes one inventory record using a fixed-width field layout:
| Columns | Width | Contents |
|---|---|---|
| 1–5 | 5 | Scott catalog number (left-justified, space-padded) |
| 6 | 1 | Condition/type code (1–9, A–H) |
| 7–12 | 6 | Quantity as a string (STR$ of numeric value) |
| 13 | 1 | Want-list flag (“Y” or “N”) |
| 14–15 | 2 | Unused |
The 6-character key K$ formed by concatenating the padded Scott number and the type code (columns 1–6) serves as a composite primary key for searching and insertion.
Menu Input Idiom
The main menu uses a polling loop rather than INPUT for single-keypress capture. Line 1028 waits until INKEY$ is non-empty, then line 1029 captures the character into O$. Line 1030 validates the key and re-loops on invalid input. The same pattern is repeated in the classification subroutine at lines 5072–5075 and in the quit confirmation at lines 2016–2022. This approach avoids the cursor and input-buffer overhead of INPUT for menu navigation.
Inventory Adjustment Logic
Option 1 (lines 2500–2990) performs a linear scan of F$ from row 1 to 800 — notably exceeding the declared dimension of 600, which would cause a subscript error if the file is full and the loop reaches row 601. When a blank row is found (line 2556), a new record is inserted; when a matching key is found (line 2558), the existing record is updated. The quantity is read back with VAL F$(N,7 TO 12), adjusted by the signed input Q, and written back via STR$ (P+Q). Line 2811 guards against negative resulting quantities by looping back to re-enter the adjustment.
Search Logic
The search routine (lines 3000–3320) accepts an optional Scott number. If left blank, E$ becomes all spaces after padding, and the condition at line 3044 — IF K$(1 TO 5)=" " AND C$=F$(N,6 TO 6) — matches all records of the selected type across the entire file, effectively doing a type-only scan. The display subroutine at 3300 uses a counter G to suppress clearing the screen after the first match, allowing multiple results to accumulate on screen before prompting for the user to press Enter at end-of-file.
Category Code Translation
The subroutine at lines 1500–1599 maps the single-character C$ to a descriptive string J$ using 18 sequential IF tests. Codes 1–9 cover numeric ASCII characters and codes A–H cover uppercase letters, giving 18 distinct stamp categories. The validation in the classification subroutine (line 5082) checks C$<"1" OR C$>"H", which in ASCII order accepts all characters from “1” (ASCII 49) through “H” (ASCII 72), inadvertently including characters like the colon, letters A–G, and digits 1–9 — the intended valid range — but also characters such as “:”, “;”, “<“, “=”, “>”, “?”, “@” which fall between “9” and “A” in ASCII. These intermediate codes would produce the default J$="?????????" translation but are otherwise harmless.
Want List
Option 3 (lines 3500–3710) iterates all 600 rows and prints entries where column 13 equals “Y”. The want-list flag is set during inventory adjustment (lines 2840–2842) via a Y/N prompt. The listing routine at line 3600 reads the type code from F$(N,6 TO 6) back into C$ before calling the translation subroutine, correctly recovering the stored category description.
Tape Save
Option 4 prompts the user to ready the tape recorder and then executes SAVE "STAM%P" at line 4016. The %P notation represents an inverse-video “P” character in the filename.
Notable Bugs and Anomalies
- The inventory adjustment loop at line
2552iteratesFOR N=1 TO 800, butF$is only dimensioned to 600 rows. AccessingF$(N,...)for N > 600 will cause a subscript error if the file fills up and no match or blank is found within the first 600 entries. - The quit confirmation at lines
2016–2022readsINKEY$twice in sequence without looping between the “Y” and “N” checks, meaning the secondINKEY$call at line2018may capture a different (or empty) value than the one that satisfied line2016. This can cause neither branch to fire, falling through to the unconditionalGOTO 70at line2022. - Line
2516prints an error for Scott numbers longer than 5 characters but does not loop back to re-prompt; the program continues to theGOSUB 5000at line2520regardless. - The search loop at line
3038iteratesFOR N=1 TO 600, consistent with the array size, unlike the adjustment loop. However, blank-row detection at line3040terminates the scan early, so stamps entered after a gap of blank rows would never be found.
Content
Source Code
50 REM "STAMP"
52 SLOW
54 DIM F$(600,15)
70 GOSUB 1000
72 IF O$="Q" THEN GOTO 2000
74 IF O$="1" THEN GOTO 2500
76 IF O$="2" THEN GOTO 3000
78 IF O$="3" THEN GOTO 3500
80 IF O$="4" THEN GOTO 4000
999 STOP
1000 CLS
1010 PRINT AT 2,12;"%S%T%A%M%P"
1012 PRINT AT 3,10;"ACCOUNTING"
1014 PRINT AT 5,4;"WHICH OPTION DO YOU DESIRE ?"
1016 PRINT AT 8,4;"%O%P%T%I%O%N";AT 8,15;"%D%E%S%C%R%I%P%T%I%O%N"
1018 PRINT AT 10,6;"1";AT 10,15;"ADJUST INVENTORY"
1020 PRINT AT 12,6;"2";AT 12,15;"SEARCH INVENTORY"
1021 PRINT AT 14,6;"3";AT 14,15;"LIST WANTED NO.S"
1022 PRINT AT 16,6;"4";AT 16,15;"SAVE INVENTORY"
1024 PRINT AT 18,6;"Q";AT 18,15;"QUIT"
1026 PRINT AT 21,0;"%E%N%T%E%R OPTION NUMBER OR Q"
1028 IF INKEY$="" THEN GOTO 1026
1029 LET O$=INKEY$
1030 IF O$<>"Q" AND O$<>"1" AND O$<>"2" AND O$<>"3" AND O$<>"4" THEN GOTO 1026
1099 RETURN
1500 REM
1501 LET J$="?????????"
1502 IF C$="1" THEN LET J$="MINT SINGLE"
1504 IF C$="2" THEN LET J$="USED SINGLE"
1506 IF C$="3" THEN LET J$="MINT BLOCK"
1508 IF C$="4" THEN LET J$="USED BLOCK"
1510 IF C$="5" THEN LET J$="MINT PLATE"
1512 IF C$="6" THEN LET J$="USED PLATE"
1514 IF C$="7" THEN LET J$="MINT SHEET"
1516 IF C$="8" THEN LET J$="MINT COIL"
1518 IF C$="9" THEN LET J$="USED COIL"
1520 IF C$="A" THEN LET J$="MINT COIL PAIR"
1522 IF C$="B" THEN LET J$="USED COIL PAIR"
1524 IF C$="C" THEN LET J$="MINT LINE PAIR"
1526 IF C$="D" THEN LET J$="USED LINE PAIR"
1528 IF C$="E" THEN LET J$="MINT IMPERF"
1530 IF C$="F" THEN LET J$="USED IMPERF"
1532 IF C$="G" THEN LET J$="USED FIRST DAY"
1534 IF C$="H" THEN LET J$="MINT STRIP"
1599 RETURN
2000 REM
2010 CLS
2012 PRINT AT 1,0;"ARE YOU SURE YOU WANT TO QUIT ?"
2014 PRINT "Y/N"
2016 IF INKEY$="" THEN GOTO 2012
2018 IF INKEY$="Y" THEN STOP
2020 IF INKEY$="N" THEN GOTO 70
2022 GOTO 70
2500 CLS
2510 PRINT "ENTER SCOTT NO. (MAX IS 5 LONG)"
2511 PRINT "IF NO NUMBER THEN ENTER YOUR NO."
2512 INPUT S$
2514 PRINT S$
2516 IF LEN S$>5 THEN PRINT "TOO BIG - RE-ENTER 5 OR LESS"
2518 IF LEN S$=0 THEN GOTO 2510
2520 GOSUB 5000
2530 CLS
2535 GOSUB 1500
2540 PRINT "NUMBER ";S$;" ";J$
2541 PRINT "ENTER THE QUANTITY(+ OR -)"
2545 INPUT Q
2547 PRINT Q
2548 LET S$=S$+" "
2549 LET S$=S$(1 TO 5)
2550 LET K$=S$+C$
2552 FOR N=1 TO 800
2554 LET X$=F$(N,1 TO 6)
2556 IF X$=" " THEN GOTO 2600
2558 IF X$=K$ THEN GOTO 2700
2560 NEXT N
2600 LET P=0
2610 LET F$(N,1 TO 6)=K$
2699 GOTO 2710
2700 LET P=VAL F$(N,7 TO 12)
2710 PRINT
2712 PRINT "OLD QUANTITY WAS ";P
2800 REM
2810 PRINT "NEW QUANTITY IS ";P+Q
2811 IF P+Q<0 THEN GOTO 2535
2812 LET F$(N,7 TO 12)=STR$ (P+Q)
2820 PRINT
2825 PRINT AT 18,0;"PLACE THIS STAMP ON WANT LIST ?"
2830 PRINT "Y/N"
2832 IF INKEY$="" THEN GOTO 2825
2840 IF INKEY$="Y" THEN LET F$(N,13 TO 13)="Y"
2842 IF INKEY$="N" THEN LET F$(N,13 TO 13)="N"
2990 GOTO 70
2999 STOP
3000 CLS
3005 LET G=0
3010 CLS
3020 PRINT "ENTER SCOTT NUMBER OR HIT ENTER"
3030 PRINT "IF NUMBER IS BLANK ENTIRE FILE"
3032 PRINT "WILL BE SCANNED"
3034 INPUT E$
3035 GOSUB 5000
3036 LET E$=E$+" "
3037 LET K$=E$(1 TO 5)+C$
3038 FOR N=1 TO 600
3040 IF F$(N,1 TO 6)=" " THEN GOTO 3200
3042 IF F$(N,1 TO 6)=K$ THEN GOSUB 3300
3044 IF K$(1 TO 5)=" " AND C$=F$(N,6 TO 6) THEN GOSUB 3300
3046 NEXT N
3050 FOR N=1 TO 100
3052 NEXT N
3055 GOTO 70
3200 IF G>0 THEN GOTO 3240
3201 CLS
3202 GOSUB 1500
3203 PRINT K$(1 TO 5);" ";J$
3205 PRINT "NO STAMPS OF THIS TYPE ON FILE"
3220 FOR N=1 TO 150
3225 NEXT N
3230 GOTO 70
3240 PRINT "FILE AT END--PRESS ENTER"
3242 INPUT Y$
3244 GOTO 70
3300 IF G>0 THEN GOTO 3302
3301 CLS
3302 LET G=G+1
3304 GOSUB 1500
3306 PRINT F$(N,1 TO 5);" ";J$;" ";"QUAN= ";F$(N,7 TO 12)
3320 RETURN
3500 REM
3510 CLS
3512 PRINT "WANT LIST"
3514 FOR N=1 TO 600
3520 IF F$(N,1 TO 5)=" " THEN GOTO 3700
3525 IF F$(N,13 TO 13)="Y" THEN GOSUB 3600
3530 NEXT N
3600 LET C$=F$(N,6 TO 6)
3610 GOSUB 1500
3612 PRINT F$(N,1 TO 5);" ";J$
3699 RETURN
3700 PRINT "FILE AT END-PRESS ENTER"
3705 INPUT Y$
3710 GOTO 70
3999 STOP
4000 CLS
4010 PRINT "SET TAPE RECORDER FOR RECORD "
4012 PRINT "MODE--START RECORDER"
4014 PRINT "PRESS ENTER WHEN READY"
4015 INPUT Y$
4016 SAVE "STAM%P"
4018 CLS
4020 GOTO 70
5000 REM
5010 CLS
5012 PRINT "CLASSIFY YOUR REQUEST BY CODE "
5014 PRINT AT 2,0;"%D%E%S%C%R%I%P%T%I%O%N";AT 2,18;"%M%I%N%T %U%S%E%D"
5016 PRINT AT 3,0;"SINGLE";AT 3,19;"1 2"
5018 PRINT AT 4,0;"BLOCK OF FOUR";AT 4,19;"3 4"
5020 PRINT AT 5,0;"PLATE BLOCK";AT 5,19;"5 6"
5022 PRINT AT 6,0;"SHEET";AT 6,19;"7 "
5024 PRINT AT 7,0;"COIL";AT 7,19;"8 9"
5026 PRINT AT 8,0;"COIL PAIR";AT 8,19;"A B"
5028 PRINT AT 9,0;"LINE PAIR";AT 9,19;"C D"
5030 PRINT AT 10,0;"IMPERFORATE";AT 10,19;"E F"
5032 PRINT AT 11,0;"FIRST DAY";AT 11,19;" G"
5034 PRINT AT 12,0;"STRIP";AT 12,19;"H "
5070 PRINT AT 20,0;"%E%N%T%E%R THE APPLICABLE CODE"
5072 IF INKEY$="" THEN GOTO 5070
5075 LET C$=INKEY$
5082 IF C$<"1" OR C$>"H" THEN GOTO 5070
5083 PRINT C$
5999 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
