ZX Profile is a database-style filing and search program that stores records in a text buffer managed largely by machine code routines embedded in REM statements.
The program allows users to add, edit, search, and print multi-field records, with each record delimited by CHR$ 23 (the ZX81 newline token) and CHR$ 134 (a graphics character used as an end-of-file marker).
Machine code entry points are called via USR at addresses such as 16546, 16602, 16624, 16674, 16688, 16738, 16780, 16820, and 16890, covering functions including string searching, record navigation, line copying, and printer output.
The DEFP (define print) mode at line 2000 lets the user configure which copy line to start from, how many lines to print, and the form spacing for hard-copy output.
Input validation at the GOSUB 1200 subroutine enforces that entry fields are either blank or contain only numeric digit tokens (codes 28–37), preventing non-numeric data from corrupting record numbers.
Program Analysis
Program Structure
The program is organized into several functional regions:
- Lines 2–16 (REM blocks): Contain the machine code routines called throughout via
USR. - Lines 17–45 (Main menu / dispatch): Display the home screen, accept a command string
X$, and branch to add, search, auto-search, or configuration modes. - Lines 75–160 (Search engine): Set up a pointer
P, call machine code searchers, and display results. - Lines 220–245 (Post-match menu): Offer Continue, Copy, Return, Edit, and New-search options after a match is found.
- Lines 300–565 (Add/Edit file entry): A line-editor loop using arrow keys (codes “6”/”7″) to move a cursor
Ythrough up to 12 data lines. - Lines 602–662 (Close/save record): Iterates lines 3–14, calls machine code to write each line into the buffer, appends the end-of-record sentinel bytes, and returns to the home screen.
- Lines 665–725 (AUTO search setup): Collects a command, optional start and end record numbers, and jumps to line 30.
- Lines 1008–1100 (Sequential record iterator): Advances through records using
RAND USR 16890, strips leading control characters, and compares against the target record number. - Lines 1200–1230 (Numeric input subroutine): Validates a 5-character field for blank or digit-only content.
- Lines 2010–2100 (DEFP — print format setup): Configures
C1(start copy line),C2(number of lines), andS(form spacing). - Lines 3000–3040 (Copy/print routine): Positions print head, POKEs
C2into address 16679, callsRAND USR 16674, and issuesLPRINTform feeds.
Machine Code Routines
All machine code is stored inside REM statements at lines 2, 13, and 16. The Z80 opcodes can be decoded from the hex data. The following USR entry points are used:
| Address | Role inferred from context |
|---|---|
16546 | Scan forward from P for a match to X$; returns match position or 1 on failure |
16602 | Secondary search step (called when B>P) |
16624 | Returns byte-length of current record for edit repositioning (P=P-USR 16624) |
16674 | Copy/print record lines (called with C2 pre-POKEd at 16679) |
16688 | Retrieve display-buffer address of next character position (used in the field-display loop at line 130) |
16738 | Write one display line into the data buffer at offset L+P |
16780 | Test some buffer condition; returns 0 to force re-search at line 121 |
16820 | Prepare or tokenize search string X$ (called after LET X$=X$) |
16890 | Advance record pointer to next record in the buffer |
The RAND USR idiom is used when the return value is not needed, discarding it without a LET assignment and avoiding an error if the routine returns an odd value.
Data Format
Records are stored in a contiguous string buffer referenced through D$ (whose length is displayed on the home screen as free slots via LEN D$-P). Each record consists of up to 12 lines of text, terminated by CHR$ 23 (0x17, the ZX81 newline token) followed by CHR$ 134 (0x86), which serves as the end-of-record sentinel. The title line of each record is prefixed with an asterisk ("*", added at line 557). The variable L holds the base address of the buffer (computed as 6+(PEEK 16400+256*PEEK 16401)), and P is the current offset within it.
Key BASIC Idioms
- Computed GOTO: Line 245 uses
GOTO 220*(Y$<>"N")-110*(Y$="")-190*(Y$="R"). Boolean expressions evaluate to 1 (true) or 0 (false), so only one term is non-zero at a time, routing control arithmetically without a chain ofIFstatements. LET X$=X$(line 40): Forces re-evaluation of the string variable, which in ZX81 BASIC can trigger internal defragmentation or re-tokenization needed before passing to the machine code at 16820.LET Y$=Y$(line 1010): Same technique applied toY$before the machine code at 16890 operates on it.- Slice assignment
INPUT Z$( TO 5): Lines 704 and 1200 use a slice target to cap input at exactly 5 characters, preventing overrun of the numeric field. VAL "number"inGO TO/GO SUB: Not present here; line targets are plain integers.
Input Validation Subroutine (Lines 1200–1230)
The subroutine at line 1200 accepts a 5-character string and checks each character’s CODE value. Digit tokens on the ZX81 have codes 28–37 (representing “0”–”9″). The condition at line 1210 sets a flag Y=2 if any character is non-blank (code ≠ 0) and outside that digit range, looping back to re-prompt. This cleanly prevents letters or punctuation from entering numeric record-number fields.
Printer and Copy Mode
The DEFP configuration (lines 2010–2100) stores three parameters: C1 (first record line to print, offset by 2), C2 (number of lines), and S (blank lines between records for form feed). At print time (line 3000), POKE 16679,C2 passes the line count directly into a machine code parameter location before calling RAND USR 16674. The FOR X=1 TO S / LPRINT / NEXT X loop at lines 3035–3037 generates the inter-record spacing on the printer.
Display Cursor Editor (Lines 504–565)
The add/edit screen implements a simple full-screen line editor. A ">" marker is displayed at row Y and immediately erased with a space character before reading INKEY$, creating a non-blocking polling loop. Keys “6” and “7” move the cursor up and down (constrained to rows 3–14). The INKEY$<>CHR$ 118 test at line 550 waits until no key is pressed (code 118 is the ZX81 NEWLINE token in the keyboard map) before proceeding to INPUT, effectively debouncing the ENTER key from the arrow-key scan.
Notable Anomalies
- Line 190 is referenced in the computed
GOTOat line 245 (-190*(Y$="R")) but does not appear in the listing. On ZX81 BASIC, branching to a non-existent line number causes execution to continue at the next line after that number — in this case line 220, which re-displays the post-match menu. This is an intentional technique to loop back. - Line 30 is referenced at line 725 but not present; execution falls through to line 31 (
CLS), which is the intended start of a fresh search. - Lines 500 and 600 are referenced (lines 32 and 520) but only line 501 and 602 exist; the same fall-through technique applies.
- The
Q$variable is used throughout (e.g., lines 506, 552) to print blank padding lines but is never explicitly assigned in the visible listing, suggesting it is initialized to a newline or spaces by the machine code setup or relies on the default empty-string value.
Source Code
2 REM 279B272A37399B 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09B9B9B359B 02AD640ED4BD8401182401AEDB1E2DA40131AFE9BCAC140EDA1CAB240C3A94022D6402B7EFE17CACE40C3C440227B40ED43D840C97C53 1 0CD1941 1 1 0C3CE40ED4B7B40C9 0ED4B E40C9 0 0ED5B7B40 1 0 0D513 31AFE17C2F840D5ED437B40 1 0 0 3131AFE86C2 841E1D1EDB0ED4B7B40C92A1040 1 6 0ED4AC92A E40C516 5C37E 8 0 0 0 0 0ED4B7B40 3 AED437B40FE17CA4641FE8CC8D7C33041 B AFE17C24641ED437B40C9 0 0 0 0 0 0 0 0 0 0 0 0 0 03954ED5B6041 1 0 0ED6B E40E5 3237EFE76C26E412B B7EFE 0CA764123368CE1 3 3C5EDB0C1C9 0 0 0 1 0 0ED6B7B40119240233E17BEC81ABEC29341131AFE9BC29641 1 1 0C9 0 0 0
13 REM ED6B C4023118240ED4B2E407EFE18CCDC41EDA0EAC041CD194122D640 0 0 0 0 03E9B12C9 0 03E9B12119140121323 B79D6 E3E9B32F341F8 1 E 0C9 0
16 REM ED6B7B403E1723BE20FC2B 1 5 03E 0EDB9E521 5 0ED42E5C1ED6B2E40ED5B1240ED5A232323EBE123EDB0C9
17 CLS
18 PRINT "%Z%X% %P%R%O%/%F%I%L%E",,,,"%E%N%T%E%R% %A% %S%E%A%R%C%H% %C%O%M%M%A%N%D","%O%R% %T%Y%P%E% %"%A%"% %T%O% %A%D%D% % % % ","% % % % % %"%S%A%V%E%"% %T%O% %S%A%V%E% % % ","SPACE OPEN: ";LEN D$-P;" SLOTS";AT 10,0;"SEPARATE MULTI-WORD","COMMANDS WITH A ""/""",,,"TYPE ""AUTO"" FOR AUTO-SEARCH"
19 POKE 16883,0
20 LET Y=0
21 PRINT TAB 5;"""DEFP"" FOR PRINT FORMAT";AT 19,0;" PRINT STATUS",,"START/NO./SPACE";TAB 2;C1-2;TAB 6;C2;TAB 12;S
22 LET C=Y
23 INPUT X$
25 IF X$="SAVE" THEN SAVE "Z%X"
26 IF X$="DEFP" THEN GOTO 2000
27 LET Z$="00000"
28 IF X$="AUTO" THEN GOTO 665
31 CLS
32 IF X$="A" THEN GOTO 500
33 IF Y THEN LET E=L
34 IF X$="" THEN GOTO 18
35 IF X$(LEN X$)="/" OR X$="SAVE" OR LEN X$>28 THEN GOTO 18
36 PRINT X$
40 LET X$=X$
45 RAND USR 16820
75 POKE 16600,P-256*INT (P/256)
80 POKE 16601,INT (P/256)
110 CLS
115 LET B=USR 16546
117 IF B>P THEN LET B=USR 16602
121 IF PEEK 16883 AND B<>1 THEN IF USR 16780=0 THEN GOTO 115
123 IF B<>1 THEN IF Y THEN GOTO 1000
124 IF Y AND VAL Z$>E THEN GOTO 1070
125 PRINT X$;TAB 0;"%F%I%L%E% %S%E%A%R%C%H",,,,TAB 1;"*";
130 LET X=USR 16688
132 IF PEEK X=140 THEN PRINT TAB 1;
140 IF PEEK X<>23 THEN GOTO 130
160 IF B<>1 AND C=1 THEN GOTO 3000
220 PRINT AT 16,0;"%H%I%T% %E%N%T%E%R% %T%O% %C%O%N%T%I%N%U%E% %S%E%A%R%C%H%I%N%G% %"%C%"% %T%O% %C%O%P%Y% % % % % % % % % % % % % % % % % % % % % %"%R%"% %T%O% %R%E%T%U%R%N% %T%O% %P%R%E%V%I%O%U%S% %F%I%L%E%S% %"%E%"% %T%O% %E%D%I%T% %T%H%I%S% %F%I%L%E% % % % % % % % % % % %"%N%"% %T%O% %B%E%G%I%N% %A% %N%E%W% %F%I%L%E% %S%E%A%R%C%H% % "
230 INPUT Y$
231 IF Y$="C" THEN GOTO 3000
232 IF Y$="E" AND B<>1 AND B<P-20 THEN GOTO 300
245 GOTO 220*(Y$<>"N")-110*(Y$="")-190*(Y$="R")
300 LET P=P-USR 16624
310 PRINT AT 0,20;"%E%D%I%T%/%F%I%L%E"
320 GOTO 504
501 PRINT "%A%D%D%/%F%I%L%E%-%-%E%N%T%E%R% %T%I%T%L%E";AT 3,1;"*"
504 LET L=6+(PEEK 16400+256*PEEK 16401)
505 LET Y=3
506 PRINT AT 16,0;"PRESS ""C"" TO CLOSE THE FILE. ARROWS MOVE THE "">"" HIT %E%N%T%E%R TO INPUT DATA";Q$;Q$;Q$
507 IF LEN Y$>=29 THEN PRINT AT 20,0;"%D%A%T%A% %T%O%O% %L%O%N%G%-%-%R%E%/%I%N%P%U%T"
508 SLOW
509 PRINT AT Y,0;">";AT Y,0;" ";
510 LET Y$=INKEY$
520 IF Y$="C" THEN GOTO 600
545 IF (Y$="6" AND Y<14) OR (Y$="7" AND Y>3) THEN LET Y=Y+SGN (6.5-VAL Y$)
550 IF INKEY$<>CHR$ 118 THEN GOTO 509
552 PRINT AT 16,0;" INPUT DATA. PRESS JUST %E%N%T%E%R TO DELETE";Q$;Q$;Q$
553 PRINT AT Y,0;"%>";
555 INPUT Y$
556 IF Y=3 AND Y$="" THEN GOTO 650
557 IF Y=3 THEN LET Y$="*"+Y$
560 IF LEN Y$<29 THEN PRINT AT Y,0;Q$;AT Y,1;Y$
562 LET Y=Y+(Y<14)-(LEN Y$>=29)
565 GOTO 506
602 FAST
605 FOR Y=3 TO 14
610 PRINT AT Y,1;
615 IF PEEK USR 16617=0 THEN GOTO 640
620 POKE 16736,(L+P)-256*INT ((L+P)/256)
630 POKE 16737,INT ((L+P)/256)
635 LET P=P+USR 16738
640 NEXT Y
645 LET P=P-1
650 POKE L+P,23
655 POKE L+P+1,134
660 FAST
662 GOTO 17
665 PRINT AT 13,0;"PRINT OUT?";Q$;Q$
670 INPUT X$
675 IF X$="Y" THEN LET C=1
700 PRINT AT 13,0;"%I%N%P%U%T% %C%O%M%M%A%N%D";Q$;Q$;Q$
701 INPUT X$
703 PRINT AT 15,0;X$;Q$;AT 16,0;"%S%T%A%R%T% %N%O%.%?";TAB 0;" OR ENTER"
704 INPUT Z$( TO 5)
706 IF Z$=" " THEN GOTO 27
707 GOSUB 1202
708 LET E=VAL Z$
709 LET L=E
710 PRINT AT 17,0;L;Q$;AT 16,18;"%E%N%D% %N%O%.%?"
717 GOSUB 1200
725 GOTO 30
1008 LET Y$=" "
1010 LET Y$=Y$
1030 RAND USR 16890
1032 IF CODE Y$<29 THEN LET Y$=Y$(2 TO )
1033 IF CODE Y$<29 THEN GOTO 1032
1040 IF Y$=((STR$ E)+" ")( TO LEN Y$) THEN GOTO 125
1060 GOTO 115
1070 LET E=E+1
1100 GOTO 36
1200 INPUT Z$( TO 5)
1202 LET Y=1
1205 FOR X=1 TO 5
1210 IF Z$=" " OR CODE Z$(X) AND (CODE Z$(X)<28 OR CODE Z$(X)>37) THEN LET Y=2
1220 NEXT X
1225 IF Y=2 THEN GOTO 1200
1230 RETURN
2010 PRINT AT 13,0;Q$;Q$;"1ST COPY LINE?"
2020 GOSUB 1200
2025 IF VAL Z$>12 THEN GOTO 2020
2030 LET C1=2+VAL Z$
2035 PRINT AT 15,0;"NO.";TAB 13;"S?"
2045 GOSUB 1200
2047 IF VAL Z$>15-C1 THEN GOTO 2045
2050 LET C2=VAL Z$
2060 PRINT AT 15,0;"% %F%O%R%M% %S%P%A%C%I%N%G%?% "
2070 GOSUB 1200
2090 LET S=VAL Z$
2100 GOTO 17
3000 PRINT AT C1,0;
3010 POKE 16679,C2
3020 RAND USR 16674
3025 LET Y$="C"
3030 IF C AND B>1 THEN LET Y$=""
3035 FOR X=1 TO S
3036 LPRINT
3037 NEXT X
3040 GOTO 232
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
