Home Asset Manager is a valuables inventory program that lets users add, delete, search, list, and save records describing household assets with fields for date, serial number, model, description, price, and location. Each record is stored as a fixed-length string in a DIM D$(MAX,RLEN) array, with record indices tracked via a packed string U$ whose characters serve as array row pointers, allowing up to 255 records. The program calculates available memory at runtime using PEEK on system variables 16386/16387 and 16412/16413 to determine how many records will fit before dimensioning the array. A machine code routine at line 9800–9830 is used for fast string searching, invoked via USR 16524 after POKEing the search key into address 16514. The listing routine at line 7000 supports output to screen, printer, or both, and can accumulate running totals for fields whose names begin with a period.
Program Analysis
Program Structure
The program is organized into clearly labeled REM-delineated sections, each reachable via computed GO TO at line 1250 (GOTO VAL "1000+1000*VAL C$"), which dispatches menu choices 1–6 to line 2000 (Add), 3000 (Delete), 4000 (Find/Search), 5000 (Save), 6000 (Quit), and 7000 (List). Control returns to the main loop at line 1000 after most operations.
| Line Range | Section |
|---|---|
| 10–860 | Initialization, screen template, field definitions |
| 1000–1250 | Main display/menu loop |
| 2000–2240 | Add record |
| 3000–3090 | Delete record |
| 4000–4550 | Search/Find |
| 5000–5080 | Save to tape |
| 6000–6020 | Quit |
| 7000–7250 | List records |
| 8000–8790 | Utility subroutines (pause, print, totals) |
| 9800–9830 | Machine code search wrapper |
Initialization and Memory Calculation
Lines 20–860 perform a detailed initialization sequence. Constants like O=PI/PI (i.e., 1) and Z=O-O (i.e., 0) are used throughout as integer substitutes, a common ZX81 BASIC idiom to avoid slow integer-parsing of literals. The screen template string S$ is built by concatenation of block-graphic border characters and spaces to create a framed display area.
At line 550, available free RAM is computed directly from system variables:
PEEK 16386 + PEEK 16387*256— RAMTOP (top of usable RAM)PEEK 16412 + PEEK 16413*256— start of the display file or variables area
After subtracting 400 bytes of overhead, MAX is calculated as how many fixed-length records of size RLEN+5 will fit, capped at 255 (line 565) because record indices are stored as single characters in U$, whose CODE values must fit in one byte.
Record Storage Scheme
Records are stored in D$(MAX,RLEN), a two-dimensional string array where each row is one fixed-length record. The record length RLEN is computed at runtime by summing the field widths F(1) through F(FLDS) (lines 510–540). Six fields are defined:
| Field | Name | Width |
|---|---|---|
| 1 | DATE | 8 |
| 2 | SERIAL | 15 |
| 3 | MODEL | 10 |
| 4 | DESCRPT | 15 |
| 5 | .PRICE | 8 |
| 6 | PLACE | 10 |
The “active record index” string U$ tracks which rows of D$ are in use. Each character’s CODE value is the row index into D$. Free row indices are tracked in E$, initialized at lines 860–880 by appending CHR$ B for each possible index from 1 to MAX. When a record is added, a character is consumed from the front of E$ and appended to U$.
Machine Code Usage
Line 10 contains a REM statement with embedded machine code bytes: itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-51725 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.6 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"B itemtype='https://schema.org/Blog' itemscope='itemscope' class="wp-singular computer_media-template-default single single-computer_media postid-51725 wp-custom-logo wp-embed-responsive wp-theme-astra wp-child-theme-astra-child ast-desktop ast-separate-container ast-left-sidebar astra-4.12.6 group-blog ast-blog-single-style-1 ast-custom-post-type ast-single-post ast-inherit-site-logo-transparent ast-hfb-header ast-full-width-primary-header ast-box-layout ast-normal-title-enabled astra-addon-4.12.4"B...\C9. This is a Z80 routine loaded into the REM area, invoked via USR 16524 at line 9820. The search subroutine (lines 9800–9830) POKEs the search character into address 16514, then calls the machine code routine and uses 255-USR 16524 to derive a result. This is a standard technique for embedding and calling machine code within a REM statement on this platform.
Search Mechanism
The search routine (lines 4000–4200) calls the Add subroutine at line 2115 to collect search key values into buffer B$. It then iterates over all records in U$, comparing each field in the record against the corresponding field from B$ after stripping trailing spaces (subroutine at 4500). A record matches if every non-empty search field matches as a prefix of the stored field value (Y$(TO LEN X$)=X$). Matching record indices are collected into L$, which is then temporarily substituted for U$ to display results via the List routine.
The delete operation at lines 3000–3090 uses the machine code search subroutine (via GOSUB 9800) to locate the current record’s index J within U$, then splices it out with string slicing.
Navigation and Display
The main loop at line 1015–1170 implements a blinking cursor effect by alternating POKEs at two display file addresses (D+67 and D+98) between two block graphic characters, toggled by FL=NOT FL. The variable D is the display file address computed from system variable at DF (16396).
Record browsing is handled by lines 8500–8530: pressing CHR$ 113 (“q”) increments CR (current record pointer into U$) and pressing CHR$ 112 (“p”) decrements it, with boundary checks. The menu selection animation at lines 1220–1240 scrolls a cursor character down the screen using a FOR loop with STEP -O (i.e., step –1).
Listing and Totals
The List routine (lines 7000–7250) outputs to screen, printer, or both depending on the user’s choice, using a shared output subroutine at line 8100 that conditionally calls LPRINT and/or PRINT. Fields whose names begin with "." (here, .PRICE) are automatically summed into array T(FLDS) at line 7115, and totals are printed by the subroutine at line 8600. A pagination counter LCNT pauses screen output when a page fills.
Notable Techniques and Idioms
O=PI/PIandZ=O-Oas integer constants 1 and 0 to speed execution and reduce program size.VAL "expression"used in computedGO TOtargets and initializations (e.g., line 105SZ=VAL "22-1-MS") — a known memory-saving technique.- Packed index string
U$using character codes as array row pointers — a compact record-tracking approach within BASIC’s string capabilities. - Free memory probing via PEEK on system variables before dimensioning arrays, allowing the program to adapt to whatever RAM is available.
- The
GOSUB 2115call from line 4030 re-uses the Add record input section mid-program, with a sentinel check at line 2180 (IF C$="3" THEN RETURN) to distinguish search-mode entry from actual adds. - The blinking cursor at lines 1152–1156 uses direct POKE to the display file rather than PRINT AT, avoiding screen flicker from redrawing.
Bugs and Anomalies
- Line 1100 uses
TAB O(TAB 1) repeatedly in a single PRINT statement to create column separations — these are literal TAB 1 calls, which may not provide the expected spacing since TAB moves to an absolute column position, not a relative one, and repeatedTAB 1calls after text has already passed column 1 would have no effect. - The field-width loop at lines 4070–4110 slices
T$andZ$starting from index 1 each time within the loop but also advances by slicing fromF(F)+1, so the first field would correctly useT$(1 TO F(1))and subsequent iterations also work correctly due to repeated re-slicing — this is slightly inefficient but functionally correct. - Line 570 prints “RECORDS AVAILIBLE” — a misspelling of “available” that would appear on screen during initialization.
- The loop at lines 4042–4044 (
FOR H=1 TO 10: NEXT H) is an empty delay loop with no effect in FAST mode (line 4056 sets FAST after this loop, but the delay itself is before FAST). It likely served as a brief pause before the FAST search begins. - Line 6020 (
GOTO 1000) is unreachable because line 6010 isSTOP, so the program can only continue from the Quit section if the user does aCONTINUEcommand.
Content
Source Code
10 REM 31B1B1B1B1B1B1B1B1B2A1040232323234E2346233A8240EDB1C9
20 DIM Q$(255)
60 SLOW
70 PRINT "INITIALIZING..."
80 LET B$=" "
85 LET LL=32
90 LET O=PI/PI
95 LET Z=O-O
100 LET MS=6
105 LET SZ=VAL "22-1-MS"
110 LET CR=O
115 LET Z$=B$+B$
117 LET Z$=B$+B$+":'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''':"
120 LET X$=": :"
125 FAST
130 FOR K=O+O+O TO SZ-O-O
140 LET Z$=Z$+X$
150 NEXT K
160 LET Z$=Z$+":..............................................................:"
170 LET X$=": % :"
180 FOR K=O TO MS
190 LET Z$=Z$+X$
200 NEXT K
210 LET Z$=Z$+"''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"
220 LET S$=Z$
230 LET X$="TIMEX VALUABLES PROGRAM"
235 LET H$="BBOOK"
240 LET S=16-LEN X$/2
250 LET S$(S TO S+LEN X$-O)=X$
260 LET DF=16396
290 LET NLEN=7
300 LET FLDS=6
305 DIM F$(FLDS,NLEN)
310 DIM F(FLDS)
320 LET F(1)=8
330 LET F(2)=15
335 LET F(3)=10
340 LET F(4)=15
350 LET F(5)=8
355 LET F(6)=10
400 LET F$(1)="DATE"
410 LET F$(2)="SERIAL"
420 LET F$(3)="MODEL"
430 LET F$(4)="DESCRPT"
440 LET F$(5)=".PRICE"
450 LET F$(6)="PLACE"
498 LET NUM=1
500 REM CALCULATE RLEN
510 LET RLEN=Z
520 FOR F=O TO FLDS
530 LET RLEN=RLEN+F(F)
540 NEXT F
545 SLOW
550 LET FRE=VAL "((PEEK 16386+PEEK 16387*256)-(PEEK 16412+PEEK 16413*256)-400)"
560 LET MAX=INT (FRE/(RLEN+5))
565 IF MAX>255 THEN LET MAX=255
570 PRINT "RECORDS AVAILIBLE: ";MAX
580 DIM D$(MAX,RLEN)
590 DIM N$(RLEN)
700 LET T2=1
710 LET T1=SZ+O
720 DIM B$(RLEN)
735 FAST
800 FOR F=O TO FLDS
810 LET S=LL*(F+O+O)+O+O
820 LET S$(S TO S+NLEN)=F$(F)+":"
830 NEXT F
840 LET U$=""
850 LET E$=""
860 FOR B=O TO MAX
870 LET E$=E$+CHR$ B
880 NEXT B
1000 REM MAIN LOOP
1005 SLOW
1010 PRINT AT Z,Z;S$
1015 PRINT AT 3,1;
1017 LET OF=O
1018 IF CR>LEN U$ OR U$="" OR CR<1 THEN GOTO 1100
1020 FOR F=O TO FLDS
1030 PRINT TAB NLEN+O+O;
1040 PRINT D$(CODE U$(CR),OF TO OF+F(F)-O)
1050 LET OF=OF+F(F)
1060 NEXT F
1100 PRINT AT SZ,O;"(1) ADD";TAB O;"(2) DELETE";TAB O;"(3) SEARCH";TAB O;"(4) SAVE";TAB O;"(5) QUIT"
1105 PRINT TAB O;"(6) LIST"
1110 PRINT AT SZ,14;"SELECT: ";
1120 LET D=PEEK DF+PEEK (DF+O)*256
1130 LET FL=1
1140 LET C$=INKEY$
1145 IF C$=CHR$ 112 OR C$=CHR$ 113 THEN GOTO 8500
1150 IF C$>="1" AND C$<="6" THEN GOTO 1200
1152 POKE D+67,CODE ":'.:"(FL+1)
1154 POKE D+98,CODE "'::."(FL+1)
1156 LET FL=NOT FL
1170 GOTO 1140
1210 PRINT AT SZ,14;" "
1220 FOR J=20 TO SZ+VAL C$-O STEP -O
1225 IF J<20 THEN PRINT AT J+1,13;"% "
1230 PRINT AT J,13;"%<"
1240 NEXT J
1250 GOTO VAL "1000+1000*VAL C$"
2000 REM ADD
2010 IF E$>"" THEN GOTO 2100
2020 PRINT AT T1,T2;"THERE ARE NO FREE";TAB T2;"RECORDS."
2030 GOTO 8000
2100 REM ADD
2110 PRINT AT Z,Z;S$
2115 LET OF=O
2120 FOR F=O TO FLDS
2130 PRINT AT 2+F,NLEN+O+O;"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"( TO F(F))
2140 INPUT I$
2150 LET B$(OF TO OF+F(F)-O)=I$
2160 PRINT AT 2+F,NLEN+O+O;B$(OF TO OF+F(F)-O)
2165 LET OF=OF+F(F)
2170 NEXT F
2180 IF C$="3" THEN RETURN
2190 LET CR=CODE E$
2200 LET E$=E$(2 TO )
2220 LET D$(CR)=B$
2230 LET U$=U$+CHR$ CR
2240 GOTO 1100
3000 REM DELETE
3020 LET E$=E$+CHR$ CR
3030 LET A$=CHR$ CR
3040 LET Q$=U$
3060 GOSUB 9800
3070 LET U$=U$( TO J-1)+U$(J+1 TO )
3080 LET D$(CR)=N$
3090 GOTO 1000
4000 REM FIND
4005 LET L$=""
4007 DIM T(FLDS)
4010 PRINT AT 0,0;S$
4020 PRINT AT SZ+2,T1;"ENTER YOUR";TAB T1;"SEARCH KEYS:"
4030 GOSUB 2115
4035 PRINT AT SZ+2,T1;" ";TAB T1;" "
4040 PRINT AT SZ+2,1;"SEARCHING.."
4042 FOR H=1 TO 10
4044 NEXT H
4050 FOR J=1 TO LEN U$
4056 FAST
4060 LET Z$=D$(CODE U$(J))
4065 LET T$=B$
4070 FOR F=1 TO FLDS
4080 LET X$=T$( TO F(F))
4090 LET Y$=Z$( TO F(F))
4095 GOSUB 4500
4100 LET T$=T$(F(F)+1 TO )
4110 LET Z$=Z$(F(F)+1 TO )
4121 IF X$="" THEN GOTO 4150
4130 IF Y$( TO LEN X$)=X$ THEN GOTO 4150
4140 GOTO 4200
4150 NEXT F
4160 LET L$=L$+U$(J)
4200 NEXT J
4201 IF L$>"" THEN GOTO 4300
4210 PRINT AT SZ+2,1;"NOT FOUND"
4220 GOSUB 8200
4230 GOTO 1000
4300 REM
4305 SLOW
4310 LET G$=U$
4320 LET U$=L$
4330 GOSUB 7000
4335 LET U$=G$
4340 GOTO 1000
4500 REM REMOVE SPACES FM X$/Y$
4510 FOR G=1 TO LEN X$
4520 IF X$(LEN X$)=" " THEN LET X$=X$( TO LEN X$-1)
4530 IF Y$(LEN Y$)=" " THEN LET Y$=Y$( TO LEN Y$-1)
4540 NEXT G
4550 RETURN
4999 STOP
5000 REM SAVE
5005 CLS
5010 PRINT TAB O;"UNDER WHAT NAME SHALL I SAVE THE INFORMATION: ";
5020 INPUT A$
5030 IF A$="" THEN LET A$=H$
5040 PRINT TAB O;TAB O;"START THE TAPE, AND THEN PRESS %E%N%T%E%R."
5050 INPUT Z$
5055 CLS
5060 SAVE A$
5070 PRINT TAB O;TAB O;"STOP THE TAPE."
5080 GOTO 8000
6000 REM QUIT
6010 STOP
6020 GOTO 1000
7000 REM LIST
7005 DIM T(FLDS)
7010 CLS
7020 PRINT "LIST TO PRINTER (P), SCREEN (S) OR BOTH (B)?"
7030 LET K$=INKEY$
7040 IF K$<>"S" AND K$<>"B" AND K$<>"P" THEN GOTO 7030
7050 LET PRI=K$<>"S"
7060 LET SCR=K$<>"P"
7070 CLS
7075 LET LCNT=INT (18/(FLDS+2))
7080 FOR K=1 TO LEN U$
7090 FAST
7100 LET Z$=D$(CODE U$(K))
7110 FOR N=1 TO FLDS
7115 IF F$(N)(1)="." THEN LET T(N)=T(N)+VAL Z$( TO F(N))
7120 LET O$=F$(N)
7130 LET O$=O$+": "+Z$( TO F(N))
7140 LET Z$=Z$(F(N)+1 TO )
7150 GOSUB 8100
7160 NEXT N
7170 LET O$=""
7180 FOR G=1 TO 2
7190 GOSUB 8100
7200 NEXT G
7205 LET LCNT=LCNT-1
7210 IF (SCR) AND (LCNT=0) THEN GOSUB 8200
7220 NEXT K
7230 LET O$="TOTAL RECORDS: "+STR$ LEN U$
7240 GOSUB 8100
7242 GOSUB 8600
7245 GOSUB 8200
7247 IF C$="3" THEN RETURN
7250 GOTO 1000
8000 REM ENTER
8010 PRINT AT 20,T2;"PRESS %E%N%T%E%R"
8020 INPUT Z$
8030 GOTO 1000
8100 REM
8110 IF PRI THEN LPRINT O$
8120 IF SCR THEN PRINT O$
8130 RETURN
8200 REM
8210 PRINT AT 20,1;"PRESS %E%N%T%E%R."
8220 INPUT W$
8230 CLS
8235 LET LCNT=INT (18/(FLDS+2))
8237 IF W$=" STOP " THEN GOTO 1000
8240 RETURN
8500 REM
8510 IF C$=CHR$ 113 THEN LET CR=CR+(CR<LEN U$)
8520 IF C$=CHR$ 112 THEN LET CR=CR-(CR>1)
8530 GOTO 1015
8600 REM TOTALS
8605 IF NOT NUM THEN RETURN
8610 LET O$=""
8620 GOSUB 8100
8630 GOSUB 8100
8640 LET O$="TOTALS:"
8650 GOSUB 8100
8660 LET O$=""
8670 GOSUB 8100
8680 FOR F=1 TO FLDS
8690 IF F$(F)(1)<>"." THEN GOTO 8750
8700 LET O$=" "+F$(F)+": "+STR$ T(F)
8710 GOSUB 8100
8750 NEXT F
8760 LET O$=""
8770 GOSUB 8100
8780 GOSUB 8100
8790 RETURN
9800 REM SEARCH
9810 POKE 16514,CODE A$
9820 LET J=255-USR 16524
9830 RETURN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
