ZX Profile

Products: ZX Pro/File
Developer(s): Thomas B. Woods
Date: 1982
Type: Cassette
Platform(s): TS 1000
Tags: Database

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:

  1. Lines 2–16 (REM blocks): Contain the machine code routines called throughout via USR.
  2. 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.
  3. Lines 75–160 (Search engine): Set up a pointer P, call machine code searchers, and display results.
  4. Lines 220–245 (Post-match menu): Offer Continue, Copy, Return, Edit, and New-search options after a match is found.
  5. Lines 300–565 (Add/Edit file entry): A line-editor loop using arrow keys (codes “6”/”7″) to move a cursor Y through up to 12 data lines.
  6. 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.
  7. Lines 665–725 (AUTO search setup): Collects a command, optional start and end record numbers, and jumps to line 30.
  8. Lines 1008–1100 (Sequential record iterator): Advances through records using RAND USR 16890, strips leading control characters, and compares against the target record number.
  9. Lines 1200–1230 (Numeric input subroutine): Validates a 5-character field for blank or digit-only content.
  10. Lines 2010–2100 (DEFP — print format setup): Configures C1 (start copy line), C2 (number of lines), and S (form spacing).
  11. Lines 3000–3040 (Copy/print routine): Positions print head, POKEs C2 into address 16679, calls RAND USR 16674, and issues LPRINT form 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:

AddressRole inferred from context
16546Scan forward from P for a match to X$; returns match position or 1 on failure
16602Secondary search step (called when B>P)
16624Returns byte-length of current record for edit repositioning (P=P-USR 16624)
16674Copy/print record lines (called with C2 pre-POKEd at 16679)
16688Retrieve display-buffer address of next character position (used in the field-display loop at line 130)
16738Write one display line into the data buffer at offset L+P
16780Test some buffer condition; returns 0 to force re-search at line 121
16820Prepare or tokenize search string X$ (called after LET X$=X$)
16890Advance 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 of IF statements.
  • 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 to Y$ 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" in GO 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 GOTO at 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.

Appears On

One individual’s cassette containing a number of programs.

Related Products

Machine language database with multi-word search, ordered displays, definable printer functions. Flexible file size, instant file access. 59 page manual...

Related Articles

Related Content

Image Gallery

Source Code

   2 REM 279B272A37399B00000000000000000009B9B9B359B02AD640ED4BD8401182401AEDB1E2DA40131AFE9BCAC140EDA1CAB240C3A94022D6402B7EFE17CACE40C3C440227B40ED43D840C97C5310CD1941110C3CE40ED4B7B40C90ED4BE40C900ED5B7B40100D51331AFE17C2F840D5ED437B401003131AFE86C2841E1D1EDB0ED4B7B40C92A1040160ED4AC92AE40C5165C37E800000ED4B7B403AED437B40FE17CA4641FE8CC8D7C33041BAFE17C24641ED437B40C9000000000000003954ED5B6041100ED6BE40E53237EFE76C26E412BB7EFE0CA764123368CE133C5EDB0C1C9000100ED6B7B40119240233E17BEC81ABEC29341131AFE9BC29641110C9000
  13 REM ED6BC4023118240ED4B2E407EFE18CCDC41EDA0EAC041CD194122D640000003E9B12C9003E9B12119140121323B79D6E3E9B32F341F81E0C90
  16 REM ED6B7B403E1723BE20FC2B1503E0EDB9E52150ED42E5C1ED6B2E40ED5B1240ED5A232323EBE123EDB0C9
  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.

Scroll to Top