--- title: "DEF 2068" id: 56103 type: "computer_media" slug: "def-2068" url: "http://localhost/computer_media/def-2068/" markdown_url: "http://localhost/computer_media/def-2068.md" published_at: "2024-07-19T01:26:34+00:00" modified_at: "2026-03-30T21:41:40+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-sodw.png" excerpt: "A menu-driven inventory manager with custom field input, automatic price calculation, low-stock warnings, and file save/load — all packed into one BASIC listing." 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: "Kenneth Majors" slug: "kenneth-majors" taxonomy: "indiv" url: "http://localhost/indiv/kenneth-majors/" genre: - name: "Business" slug: "business" taxonomy: "genre" url: "http://localhost/type/business/" - name: "Database" slug: "database" taxonomy: "genre" url: "http://localhost/type/database/" media_type: "Program" programmers: - name: "Kenneth Majors" slug: "kenneth-majors" taxonomy: "indiv" url: "http://localhost/indiv/kenneth-majors/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Def2068%20%281985%29%28Majors%2C%20Kenneth%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240718-sodw.png" related_content: - id: 54742 title: "DEF 2068 instructions" type: "document" url: "http://localhost/document/def-2068-instructions/" media_type_tags: "Business, Database" --- This program is a simple inventory database manager that stores up to ten records in a two-dimensional string array, each record holding fields such as stock number, description, supplier, quantity, unit price, retail price, low limit, and back-order quantity. Records are entered through a custom character-by-character input routine at line 1500 that supports backspace (CHR$ 12) and fixed-width field formatting with both zero-padded numeric and space-padded text fields. A calculated extended price field is derived automatically by multiplying the unit price by quantity using string-to-value conversion. The program supports searching by stock number, deleting records with array compaction, and saving or loading the data file with an auto-start line. Screen$ reads are used to retrieve displayed field values for the low-stock warning comparison rather than storing them in separate variables. *** ## Program Analysis ### Program Structure The program is organized into distinct subroutine blocks, each starting at a recognizable line number boundary, with a main menu loop at line 9040 dispatching via `GO SUB` to each module: | Lines | Module | | --- | --- | | 1001–1080 | Add Records | | 1500–1535 | Character-by-character field input subroutine | | 3001–3190 | Search / Display record | | 5001–5025 | Delete record | | 8000–8055 | Load / Save file | | 9040–9090 | Main menu loop | Line 5 initializes the record pointer `p` to 1, and line 10 immediately jumps to the main menu at 9040. Line 2 allocates the record array `e$(10,100)`, providing storage for up to ten records of 100 characters each. ### Data Storage Format Each record is serialized into a single string slot of `e$`. Fields are concatenated into `a$` with `*` as an inter-field delimiter and `/` appended at the end of a complete record (line 1060). The stock number field (row 2, `x=2`) is zero-padded to 9 characters using `b$="000000000"`; text fields at `x>=4` are space-padded to field width using `c$` (20 spaces). This means the first 9 characters of each record are always the zero-padded stock number, enabling the direct substring comparison at line 3027. ### Custom Input Routine (Lines 1500–1535) Rather than using the built-in `INPUT` for field data, the program implements its own keystroke loop. `PAUSE 0` followed by `INKEY$` at line 1500 is a standard efficient keypress-wait idiom. Key behaviors include: - CHR$ 13 (ENTER) accepts the field when at least one character has been entered (`k<>0`). - CHR$ 12 (DELETE/backspace) steps back one position and restores the dash placeholder, trimming `k$`. - Characters below CHR$ 32 (control codes) are ignored. - Printable characters are echoed to screen and appended to `k$`; the field auto-accepts when the cursor reaches the field length `l`. The field descriptors are loaded via `READ` from the `DATA` statement at line 1015, supplying the field width `l` and label `l$` for each of the eight fields. The loop variable `x` tracks screen row (incrementing by 2 per field) and `y=11` is the starting column for input areas. ### Automatic Extended Price Calculation Lines 1532–1533 implement an automatic extended price calculation. When the unit price field is entered (`x=8`), its value is saved in `f$`. When either the quantity (`x=10`) or another price field (`x=12`) is processed, the product `VAL f$ * VAL k$` is computed, converted back to a string with `STR$`, left-padded to 9 characters, and appended to the record as an extra `*`-delimited field. This stores the extended price without requiring a separate variable in the record layout. ### Record Display and SCREEN$ Trick The search/display module (lines 3060–3145) walks the stored record string character by character, printing non-delimiter characters to a two-column display. Rather than storing the quantity or low-limit values in variables for the reorder warning, the program uses `SCREEN$` (lines 3120–3140) to read the displayed digits directly back from the screen — quantity from row 8 and low limit from row 14. If the quantity string is not blank and the low limit is greater than or equal to the quantity, the low-limit value is flashed on screen (line 3145) as a reorder alert. ### Record Deletion and Array Compaction Deletion (lines 5001–5025) first calls `GO SUB 3000` (the search routine) to locate and display the record, then prompts for confirmation. On confirmation, it clears the target slot with `LET e$(x)=c$` and shifts all subsequent records down by one position in a `FOR` loop, decrementing `p` to maintain the record count. The condition at line 5023 contains a logical bug: `IF x$<>"Y" OR x$<>"y"` is always true (a string cannot equal both “Y” and “y” simultaneously), so the loop to add more deletions at line 5025 is never reached. ### File Save and Load The save/load module at line 8000 uses plain `LOAD k$` and `SAVE k$ LINE 9040`, saving the program with an auto-start at the main menu (line 9040). Because the array `e$()` is part of the program variables, it is preserved in the saved file, effectively implementing persistent data storage. ### Notable BASIC Idioms - `VAL "number"` is used pervasively in `GO TO`, `GO SUB`, and arithmetic contexts as a memory-saving technique, storing a string token rather than a floating-point number in the program. - The `DATA` statement at line 1015 mixes numeric literals written as `VAL "n"` with string literals, which is valid since `READ` accepts both forms. - Line 3075 uses the expression `3100+(20*(e$(x,z+1)="/"))` to compute a branch target — multiplying the boolean result of a string comparison by 20 to select between line 3100 and line 3120. - The `RESTORE` calls before looping back to line 1001 reset the `DATA` pointer so the field descriptors can be re-read on each new record entry. ### Bugs and Anomalies - **Line 9050**: calls `GO SUB 1000`, but the add-records module starts at line 1001. Line 1000 does not exist; the interpreter will fall through to line 1001, so this works in practice but is technically targeting a non-existent line. - **Line 3027**: references `GO TO 3060` without `VAL`, inconsistent with the rest of the program’s style but functionally correct. - **Line 3185**: `GO TO 3000` targets a non-existent line; execution falls through to 3001, the search module — an intentional well-known technique. - **Line 5023**: the condition `x$<>"Y" OR x$<>"y"` is a tautology (always true), meaning the loop for repeated deletions at line 5025 is unreachable. The intended logic was likely `AND` rather than `OR`. - **Line 3100**: the compound `IF` condition `IF y=10 AND b=10 OR y=12 AND b=10` relies on operator precedence; `AND` binds tighter than `OR`, so this evaluates as `(y=10 AND b=10) OR (y=12 AND b=10)`, which is the likely intended meaning. ## Source Code ``` 2 DIM e$(10,100) 5 LET p=VAL "1" 10 GO TO VAL "9000" 1001 CLS : LET a$="": LET k$="": LET b$="000000000": LET c$=" " 1005 PRINT AT 0,5; INVERSE 1;"Add records" 1009 LET x=VAL "2": LET y=VAL "11": FOR b=VAL "1" TO VAL "8": READ l,l$ 1010 PRINT AT x,VAL "0";l$;: FOR k=VAL "1" TO l: PRINT AT x,y+k;CHR$ VAL "45";: NEXT k: LET k=VAL "1": GO SUB VAL "1500": NEXT b 1015 DATA VAL "9","Stock# ",VAL "20","Discript ",VAL "11","Supplier ",VAL "4","Qty ",VAL "6","Unit Price ",VAL "6","Retail Price ",VAL "3","Low Limit ",VAL "3","Back Order " 1050 INPUT INVERSE 1;"Hit ENTER to log this record or C to reenter it",,x$ 1055 IF x$="C" OR x$="c" THEN RESTORE : GO TO VAL "1001" 1060 LET a$=a$+"/": LET e$(p, TO 100)=a$: LET p=p+1 1065 IF p>=10 THEN PRINT ; FLASH 1;"File Full": PAUSE 500: RETURN 1070 INPUT INVERSE 1;"More? Y/N",x$ 1075 IF x$="N" OR x$="n" THEN RESTORE : RETURN 1080 RESTORE : GO TO VAL "1001" 1500 PAUSE VAL "0": LET q$=INKEY$ 1505 IF q$=CHR$ VAL "13" AND k<>VAL "0" THEN GO TO VAL "1527" 1510 IF q$=CHR$ VAL "12" AND k>VAL "0" THEN LET k=k-1: PRINT AT x,y+k;CHR$ VAL "45": LET k$=k$( TO k-1): GO TO VAL "1500" 1515 IF q$=VAL "4" THEN LET k$=k$+c$( TO l-LEN k$) 1530 IF x>VAL "2" AND x<=VAL "16" THEN LET a$=a$+"*"+k$ 1532 IF x=VAL "8" THEN LET f$=k$ 1533 IF x=VAL "10" OR x=VAL "12" THEN LET k$=STR$ (VAL f$*VAL k$): LET k$=c$( TO VAL "9"-LEN k$)+k$: LET a$=a$+"*"+k$ 1535 LET x=x+VAL "2": LET k$="": RETURN 3001 CLS : PRINT INVERSE 1;"Search for a Record",: LET l$="": LET q$="" 3005 INPUT INVERSE 1;"Enter a Stock Number",x$ 3010 IF x$="" THEN GO TO VAL "3005" 3015 LET x$=b$( TO VAL "9"-LEN x$)+x$ 3025 FOR x=VAL "1" TO p 3027 IF e$(x,1 TO 9)=x$ THEN GO TO 3060 3035 NEXT x 3040 PRINT "File not found": PAUSE 50: RETURN 3060 LET y=VAL "2": PRINT AT VAL "2",VAL "0";"Stock #"''"Discript"''"Supplier"''"Qty"''"Unit Price"''"Rtl Price"''"Low limit"''"Bck Order" 3063 LET d=VAL "0": LET b=VAL "10" 3065 FOR z=VAL "1" TO VAL "100" 3070 IF e$(x,z)<>"*" THEN PRINT AT y,b+(z-x)-d;e$(x,z); 3075 IF e$(x,z+1)="*" OR e$(x,z+1)="/" THEN GO TO 3100+(20*(e$(x,z+1)="/")) 3080 NEXT z 3085 GO TO VAL "3120" 3100 IF y=10 AND b=10 OR y=12 AND b=10 THEN PRINT TAB VAL "18";"Ext ";: LET b=VAL "21": LET d=z-x: NEXT z 3103 PRINT : LET y=y+VAL "2": LET d=z-x: LET b=VAL "10" 3105 NEXT z 3120 FOR z=VAL "1" TO VAL "4": LET q$=q$+SCREEN$ (8,9+z): NEXT z 3140 FOR z=VAL "1" TO VAL "3": LET l$=l$+SCREEN$ (14,9+z): NEXT z 3143 IF q$=" " THEN GO TO VAL "3150" 3145 IF VAL l$>=VAL q$ THEN PRINT FLASH VAL "1";AT VAL "14",VAL "10";l$ 3175 INPUT INVERSE 1;"Press enter for new search","Q to quit",,"C to copy",,k$ 3180 IF k$="Q" OR k$="q" THEN RETURN 3185 IF k$="" THEN GO TO 3000 3188 IF k$="c" OR k$="C" THEN COPY 3190 GO TO 3175 5001 PAPER 2: INK 7: CLS : PRINT TAB 12; FLASH VAL "1";"WARNING": PRINT TAB VAL "4";"This will erase data!!"; PAPER 7; INK 0: PAUSE 100: GO SUB VAL "3000": INPUT INVERSE 1;"Delete this? Y/N",x$ 5010 IF x$="N" OR x$="n" THEN RETURN 5020 LET e$(x)=c$: FOR l=x TO p: LET e$(l)=e$(l+1): NEXT l: LET p=p-1 5023 INPUT INVERSE 1;"More? Y/N ";x$: IF x$<>"Y" OR x$<>"y" THEN PAPER VAL "7": INK VAL "0": RETURN 5025 GO TO VAL "5001" 8000 CLS : INPUT INVERSE 1;"L = load file S = save file",k$ 8010 IF k$="L" OR k$="l" THEN INPUT INVERSE 1;"enter file name ";k$: LOAD k$ 8050 IF k$="S" OR k$="s" THEN INPUT INVERSE 1;"enter file name ";k$: SAVE k$ LINE 9040 8055 RETURN 9040 CLS : PRINT INVERSE 1;" Main Menu", 9045 PRINT AT 7,VAL "5";"A = Add Records"''TAB VAL "5";"D = Delete a Record"''TAB VAL "5";"S = Search for a Record"''TAB VAL "5";"Q = Quit" 9047 INPUT INVERSE 1;"Select Option and press ENTER",o$ 9050 IF o$="A" OR o$="a" THEN GO SUB VAL "1000" 9060 IF o$="D" OR o$="d" THEN GO SUB VAL "5000" 9070 IF o$="S" OR o$="s" THEN GO SUB VAL "3000" 9080 IF o$="Q" OR o$="q" THEN GO SUB VAL "8000" 9090 GO TO VAL "9040" ```