Record Formatter

This file is part of and Timex Sinclair Public Domain Library Tape 1003. Download the collection to get this file.
Developer(s): Tim Ward
Date: 198x
Type: Program
Platform(s): TS 1000
Tags: Database

Record Formatter Ver. 1 is a ZX81/TS1000 database management program that allows users to define up to 64 custom fields, then enter, display, edit, and print records stored in a 1,459-character string array. Tape I/O is handled by two machine-code routines embedded in the line-10 REM statement: one at USR 16514 writes records to tape and one at USR 16549 reads them back, called via RAND USR. The program uses a packed record layout where each field’s start and end positions are tracked in parallel numeric arrays A() and B(), allowing substring slicing of the master string A$(1). A directory-printing routine compresses data by stripping redundant spaces before sending output to the printer via LPRINT.


Program Analysis

Program Structure

The program is organized into clearly labeled sections using inverse-text REM statements as headers. Execution begins at line 10 (which contains machine code) and proceeds through initialization before entering the main menu loop.

  1. Lines 10–20: Machine code in REM; program identity label
  2. Lines 30–80: Array and variable initialization
  3. Lines 100–180: Field definition loop (up to 64 fields)
  4. Lines 190–300: Print record layout to printer
  5. Lines 310–550: Main menu and dispatch
  6. Lines 560–760: Enter new record subroutine
  7. Lines 770–1140: Change a record subroutine
  8. Lines 1150–1460: Display a record subroutine
  9. Lines 1470–1680: Print a record subroutine
  10. Lines 1690–2010: Print data tape directory subroutine
  11. Lines 2020–2110: Copy data tape subroutine
  12. Lines 2120–2200: Copy program subroutine
  13. Lines 2210–2270: End program subroutine
  14. Lines 2280–2400: Read record from tape subroutine
  15. Lines 2410–2540: Write record to tape subroutine
  16. Lines 2550–2590: Input checker subroutine (Y/N/M)
  17. Lines 2600–2610: SAVE and restart

Machine Code Routines

Two machine-code routines are embedded in the REM statement at line 10. They are invoked via RAND USR 16514 (write record) and RAND USR 16549 (read record). The ZX81 system variables place the start of the BASIC program at address 16509 (407Fh); the REM data begins at offset 5 (after the line number, length, and REM token), so the routines land at 16514 and 16549 respectively.

Disassembly of the REM bytes reveals two distinct routines:

OffsetAddressBytesMnemonicNotes
016514CD E7 02CALL 02E7hZX81 ROM: slow-speed display/sync entry
31651706 0ELD B,0EhLoop counter = 14
51651921 00 FFLD HL,FF00hStart of candidate address range
8165222BDEC HLStep HL down
9165237CLD A,H
1016524B5OR LCheck HL=0
111652520 FBJR NZ,-5Loop until zero
131652710 F6DJNZ -10Outer timing loop
15165292A 10 40LD HL,(4010h)Load A$ descriptor pointer
181653223INC HL
19165334ELD C,(HL)Low byte of length
201653423INC HL
211653546LD B,(HL)High byte of length
221653623INC HL
2316537C5PUSH BCSave byte count
24165385ELD E,(HL)Data address low
2516539CD 1F 03CALL 031FhROM: output byte to tape
2816542C1POP BC
29165430BDEC BC
301654478LD A,B
3116545B1OR CCheck BC=0
321654620 F4JR NZ,-12Write loop
3416548C9RETEnd of write routine
3516549CD E7 02CALL 02E7hRead routine starts here
38165522A 10 40LD HL,(4010h)Load A$ descriptor
1E 08 DB FE D3 FF…(tape read loop)Reads bytes from EAR port, reconstructs A$

The read routine polls the EAR port (DB FE = IN A,(FEh)) and outputs to FFh (D3 FF = OUT (FFh),A), using rotate and bit-test instructions to decode the Manchester-like tape signal. The length of data transferred is derived from the A$ array descriptor at address 4010h, so the routines always read/write exactly 1,459 bytes — the full record.

Data Layout

The master record is held in A$(1), a single 1,459-character string (the first and only row of the 1×1459 DIM). Field positions are pre-computed during setup:

  • A(X) — start position of field X within A$(1)
  • B(X) — end position of field X within A$(1)
  • C(X) — declared length of field X
  • B$(X) — 32-character field title (DIM B$(64,32))

Field data is read and written using ZX81 substring slicing: A$(1,A(X) TO B(X)). A temporary single-row string C$(1,C(X)) is re-DIMmed for each field during entry and editing to enforce the correct field width.

Menu and Navigation

The main menu at line 310 uses a single-keypress idiom: INKEY$ is tested in a tight loop at line 450, then captured at line 460 and dispatched via a cascade of IF … THEN GOSUB / GOTO statements. The reusable input checker subroutine at line 2550 accepts only Y, N, or M (Menu return), providing consistent three-way navigation throughout.

Key BASIC Idioms

  • RAND USR — used to call machine-code routines without storing a return value, discarding the USR result safely.
  • Re-DIM inside loopDIM C$(1,C(X)) at lines 630 and 1010 re-allocates C$ each iteration to match the current field’s length, ensuring INPUT fills exactly the right number of characters.
  • VAL-style line targets — line numbers in GOTO/GOSUB are written with leading zeros (e.g., GOTO 0450, GOSUB 0560), a common ZX81 style that has no runtime effect but aids readability.
  • FAST/SLOW bracketing — printer output blocks (lines 190–300 and 1610–1670) are wrapped in FAST/SLOW to disable the display interrupt and speed up LPRINT operations.

Directory Space-Compression Routine

The tape directory printer (lines 1930–1970) implements a simple run-length space compressor: it builds D$ by copying non-space characters from C$(1) and inserting a single space only when the next character is a space, collapsing multiple spaces into one. This produces compact, human-readable directory entries from fixed-width fields.

Notable Bugs and Anomalies

  • Line 1000 unreachable checkIF I$="N" AND X=65 THEN GOTO 1130 follows IF I$="N" THEN NEXT X at line 990. If I$="N", control jumps to NEXT X and never reaches line 1000; the X=65 boundary check is therefore dead code. The loop termination after all 64 fields are processed falls through to line 1130 naturally via the FOR/NEXT mechanism.
  • Field count vs. tape block size — The program defines up to 64 fields but the directory loop at line 1880 is hard-coded to FOR X=1 TO 35, implying the data tape is expected to hold exactly 35 records. There is no run-time check that the total declared field lengths do not exceed 1,459.
  • Line 1430 double-read of INKEY$ — After the wait loop at line 1420 confirms a key is pressed, line 1430 calls INKEY$ again. On a fast machine this could return an empty string if the key was released between the two polls, potentially failing to detect the M keypress.
  • Line 2610GOTO 300 targets line 300 (SLOW), not the menu at line 310, meaning after a SAVE the machine briefly executes SLOW before falling into the menu — harmless but slightly inconsistent with the direct GOTO 0310 used elsewhere.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10122 – 10175.

Related Products

Related Articles

Related Content

Image Gallery

Source Code

  10 REM CDE726E210FF2B7CB520FB10F62A1040234E234623C55ECD1F3C1B78B120F4C9CDE722A1040234E234623C51E8DBFED3FF1730F9E9461ADDBFE17CB797938F510F5204FE5630E43FCB161D20DEC1B78B1C818D3
  20 REM %P%R%O%G%R%A%M% %S%E%T% %U%P% % % % % % % % % 
  30 DIM A$(1,1459)
  40 DIM B$(64,32)
  50 DIM A(64)
  60 DIM B(64)
  70 DIM C(64)
  80 LET M=310
  90 LET W=1
 100 FOR X=1 TO 64
 110 PRINT AT 12,0;"ENTER FIELD TITLE       (32 POS)"
 120 INPUT B$(X)
 130 PRINT AT 12,0;"ENTER LENGTH OF FIELD           "
 140 INPUT C(X)
 150 LET A(X)=W
 160 LET B(X)=W+C(X)-1
 170 LET W=W+C(X)
 180 NEXT X
 190 FAST 
 200 LPRINT "RECORD LAYOUT"
 210 LPRINT 
 220 LPRINT "FIELD NAME"
 230 LPRINT "ST POS  END POS  LENGTH FLD NBR."
 240 LPRINT "--------------------------------"
 250 FOR X=1 TO 64
 260 LPRINT B$(X)
 270 LPRINT A(X);TAB 8;B(X);TAB 17;C(X);TAB 25;X
 280 LPRINT 
 290 NEXT X
 300 SLOW 
 310 REM %M%E%N%U% % % % % % % % % % % % % % % % % % % 
 320 CLS 
 330 PRINT TAB 5;"RECORD FORMATTER VER.1"
 340 PRINT TAB 3;"COPYRIGHT 1985 TIM L. WARD",,,
 350 PRINT "DO YOU WISH TO..",,,
 360 PRINT "1)...ENTER A NEW RECORD?",,,
 370 PRINT "2)...CHANGE A RECORD?",,,
 380 PRINT "3)...DISPLAY A RECORD?",,,
 390 PRINT "4)...PRINT A RECORD?",,,
 400 PRINT "5)...PRINT DATA TAPE DIRECTORY?",,,
 410 PRINT "6)...COPY DATA TAPE?",,,
 420 PRINT "7)...COPY PROGRAM?",,,
 430 PRINT "8)...END PROGRAM?",,,
 440 PRINT "ENTER NBR. OF SELECTION"
 450 IF INKEY$="" THEN GOTO 0450
 460 LET I$=INKEY$
 470 IF I$="1" THEN GOSUB 0560
 480 IF I$="2" THEN GOSUB 0770
 490 IF I$="3" THEN GOSUB 1150
 500 IF I$="4" THEN GOSUB 1470
 510 IF I$="5" THEN GOSUB 1690
 520 IF I$="6" THEN GOSUB 2020
 530 IF I$="7" THEN GOTO 2120
 540 IF I$="8" THEN GOSUB 2210
 550 GOTO 0310
 560 REM %E%N%T%E%R% %A% %N%E%W% %R%E%C%O%R%D% % % % % 
 570 CLS 
 580 PRINT ,,,,,,"DO YOU WISH TO ADD A NEW RCD?"
 590 PRINT TAB 11;"(Y, N, OR M)"
 600 GOSUB 2550
 610 IF I$<>"Y" THEN RETURN 
 620 FOR X=1 TO 64
 630 DIM C$(1,C(X))
 640 CLS 
 650 PRINT AT 0,6;"%E%N%T%E%R% %A% %N%E%W% %R%E%C%O%R%D"
 660 PRINT AT 2,0;"PLEASE ENTER INDIVIDUALS:"
 670 PRINT AT 4,0;B$(X)
 680 INPUT C$(1)
 690 PRINT AT 6,0;C$(1)
 700 PRINT "IS THIS CORRECT? (Y  OR N)"
 710 GOSUB 2550
 720 IF I$<>"Y" THEN GOTO 0640
 730 LET A$(1,A(X) TO B(X))=C$(1)
 740 NEXT X
 750 GOSUB 2410
 760 RETURN 
 770 REM %C%H%A%N%G%E% %A% %R%E%C%O%R%D% % % % % % % % 
 780 CLS 
 790 PRINT AT 0,8;"%C%H%A%N%G%E% %A% %R%E%C%O%R%D"
 800 PRINT AT 2,0;"DO YOU WISH TO CHANGE THIS RCD?"
 810 PRINT AT 4,11;"(Y, N, OR M)",,,
 820 FOR X=1 TO 5
 830 PRINT B$(X)
 840 PRINT A$(1,A(X) TO B(X))
 850 NEXT X
 860 GOSUB 2550
 870 IF I$="M" THEN RETURN 
 880 IF I$="Y" THEN GOTO 0920
 890 GOSUB 2410
 900 GOSUB 2280
 910 GOTO 0770
 920 FOR X=1 TO 64
 930 CLS 
 940 PRINT AT 0,8;"%C%H%A%N%G%E% %A% %R%E%C%O%R%D"
 950 PRINT AT 2,0;"CHANGE THIS FIELD? (Y OR N)"
 960 PRINT AT 4,0;B$(X)
 970 PRINT A$(1,A(X) TO B(X))
 980 GOSUB 2550
 990 IF I$="N" THEN NEXT X
 1000 IF I$="N" AND X=65 THEN GOTO 1130
 1010 DIM C$(1,C(X))
 1020 CLS 
 1030 PRINT AT 0,8;"%C%H%A%N%G%E% %A% %R%E%C%O%R%D"
 1040 PRINT AT 2,0;"PLEASE ENTER INDIVIDUALS:"
 1050 PRINT AT 4,0;B$(X)
 1060 INPUT C$(1)
 1070 PRINT AT 6,0;C$(1)
 1080 PRINT "IS THIS CORRECT? (Y OR N)"
 1090 GOSUB 2550
 1100 IF I$<>"Y" THEN GOTO 1020
 1110 LET A$(1,A(X) TO B(X))=C$(1)
 1120 NEXT X
 1130 GOSUB 2410
 1140 RETURN 
 1150 REM %D%I%S%P%L%A%Y% %A% %R%E%C%O%R%D% % % % % % % 
 1160 CLS 
 1170 PRINT AT 0,8;"%D%I%S%P%L%A%Y% %A% %R%E%C%O%R%D"
 1180 PRINT AT 2,0;"DO YOU WISH TO DISPLAY THIS RCD?"
 1190 PRINT AT 4,11;"(Y, N, OR M)"
 1200 FOR X=1 TO 5
 1210 PRINT B$(X)
 1220 PRINT A$(1,A(X) TO B(X))
 1230 NEXT X
 1240 GOSUB 2550
 1250 IF I$="M" THEN RETURN 
 1260 IF I$="Y" THEN GOTO 1290
 1270 GOSUB 2280
 1280 GOTO 1150
 1290 CLS 
 1300 FOR X=1 TO 64
 1310 PRINT AT 0,8;"%D%I%S%P%L%A%Y% %A% %R%E%C%O%R%D"
 1320 PRINT AT 2,0;"RECORD DISPLAYED IS FOR..."
 1330 PRINT AT 4,0;"GEN/CODE NBR. ";A$(1, TO 5)
 1340 PRINT AT 5,0;A$(1,6 TO 20)
 1350 PRINT AT 6,0;A$(1,21 TO 35)
 1360 PRINT AT 7,0;A$(1,36 TO 50)
 1370 PRINT AT 8,0;A$(1,51 TO 65)
 1380 PRINT AT 15,0;"%P%R%E%S%S% %E%N%T%E%R% %F%O%R% %N%E%X%T% %F%I%E%L%D% % % % % % "
 1390 PRINT AT 16,0;"%P%R%E%S%S% %"%M%"% %T%O% %R%E%T%U%R%N% %T%O% % %M%E%N%U% % % % "
 1400 PRINT AT 10,0;B$(X)
 1410 PRINT AT 12,0;A$(1,A(X) TO B(X))
 1420 IF INKEY$="" THEN GOTO 1420
 1430 IF INKEY$="M" THEN RETURN 
 1440 PRINT AT 12,0;"                                "
 1450 NEXT X
 1460 RETURN 
 1470 REM %P%R%I%N%T% %A% %R%E%C%O%R%D% % % % % % % % % 
 1480 CLS 
 1490 PRINT AT 0,8;"%P%R%I%N%T% %A% %R%E%C%O%R%D"
 1500 PRINT AT 2,0;"DO YOU WISH TO PRINT THIS RCD?"
 1510 PRINT AT 4,11;"(Y, N, OR M)",,,
 1520 FOR X=1 TO 5
 1530 PRINT B$(X)
 1540 PRINT A$(1,A(X) TO B(X))
 1550 NEXT X
 1560 GOSUB 2550
 1570 IF I$="Y" THEN GOTO 1610
 1580 IF I$="M" THEN RETURN 
 1590 GOSUB 2280
 1600 GOTO 1470
 1610 FAST 
 1620 FOR X=1 TO 64
 1630 LPRINT B$(X)
 1640 LPRINT A$(1,A(X) TO B(X))
 1650 LPRINT 
 1660 NEXT X
 1670 SLOW 
 1680 RETURN 
 1690 REM %P%R%I%N%T% %D%/%T% %D%I%R%E%C%T%O%R%Y% % % % 
 1700 CLS 
 1710 PRINT AT 0,5;"%D%A%T%A% %T%A%P%E% %D%I%R%E%C%T%O%R%Y"
 1720 PRINT AT 4,0;"IS THE PRINTER ON ? (Y, N, OR M)"
 1730 GOSUB 2550
 1740 IF I$="M" THEN RETURN 
 1750 IF I$="Y" THEN GOTO 1790
 1760 PRINT AT 12,0;"PLEASE TURN THE PRINTER ON"
 1770 PRINT AT 14,0;"PRESS ENTER WHEN PRINTER IS ON"
 1780 INPUT I$
 1790 CLS 
 1800 PRINT AT 0,5;"%D%A%T%A% %T%A%P%E% %D%I%R%E%C%T%O%R%Y"
 1810 PRINT AT 4,0;"PLACE OLD DATA TAPE IN TAPE DECK"
 1820 PRINT AT 6,0;"PRESS ""PLAY"" ON TAPE DECK"
 1830 PRINT AT 8,0;"PRESS ""ENTER"" ON COMPUTER"
 1840 INPUT I$
 1850 LPRINT "% % % % % % % DATA TAPE DIRECTORY% % % % % % ",,
 1860 LPRINT "GEN/"
 1870 LPRINT "CODE  FIRST/LAST NAME",,,
 1880 FOR X=1 TO 35
 1890 RAND USR 16549
 1900 DIM C$(1,36)
 1910 LET C$(1)=A$(1,1 TO 5)+" "+A$(1,6 TO 20)+A$(1,51 TO 65)
 1920 LET D$=""
 1930 FOR Y=1 TO 36
 1940 IF C$(1,Y)=" " THEN GOTO 1970
 1950 LET D$=D$+C$(1,Y)
 1960 IF C$(1,Y+1)=" " THEN LET D$=D$+" "
 1970 NEXT Y
 1980 LPRINT D$
 1990 LPRINT 
 2000 NEXT X
 2010 RETURN 
 2020 REM %C%O%P%Y% %D%A%T%A% %T%A%P%E% % % % % % % % % 
 2030 CLS 
 2040 PRINT ,,,,,,"HOW MANY RECORDS DO YOU WISH TO",,,"COPY? (1 TO 35)"
 2050 INPUT Z
 2060 IF Z>35 THEN GOTO 2020
 2070 FOR X=1 TO Z
 2080 GOSUB 2280
 2090 GOSUB 2410
 2100 NEXT X
 2110 RETURN 
 2120 REM %C%O%P%Y% %P%R%O%G%R%A%M% % % % % % % % % % % 
 2130 CLS 
 2140 PRINT ,,,,,,"PLACE BLANK PROGRAM TAPE IN TAPE",,"DECK, PRESS ""PLAY/RECORD"" ON",,,"TAPE DECK, PRESS ""ENTER"" ON",,,"COMPUTER."
 2150 INPUT I$
 2160 CLS 
 2170 SAVE "FORMATTE%R"
 2180 PRINT ,,,,,,"PRESS ""STOP"" ON TAPE DECK",,,"PRESS ""ENTER"" ON COMPUTER."
 2190 INPUT I$
 2200 GOTO 0310
 2210 REM %E%N%D% %P%R%O%G%R%A%M% % % % % % % % % % % % 
 2220 CLS 
 2230 PRINT ,,,,,,"ARE YOU SURE YOU WANT TO QUIT?"
 2240 PRINT TAB 11;"(Y, N, OR M)"
 2250 GOSUB 2550
 2260 IF I$="Y" THEN NEW 
 2270 RETURN 
 2280 REM %R%E%A%D% %R%E%C%O%R%D% %O%N% %T%A%P%E% % % % 
 2290 CLS 
 2300 PRINT AT 2,0;"PLACE OLD DATA TAPE IN TAPE DECK"
 2310 PRINT AT 4,0;"PRESS ""PLAY"" ON TAPE DECK"
 2320 PRINT AT 6,0;"PRESS ""ENTER"" ON COMPUTER"
 2330 INPUT Z$
 2340 RAND USR 16549
 2350 CLS 
 2360 SLOW 
 2370 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
 2380 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
 2390 INPUT Z$
 2400 RETURN 
 2410 REM %W%R%I%T%E% %R%E%C%O%R%D% %O%N% %T%A%P%E% % % 
 2420 CLS 
 2430 PRINT AT 2,0;"PLACE NEW DATA TAPE IN TAPE DECK"
 2440 PRINT AT 4,0;"PRESS ""PLAY/RECORD"" ON TAPE"
 2450 PRINT "DECK"
 2460 PRINT AT 7,0;"PRESS ""ENTER"" ON COMPUTER"
 2470 INPUT Z$
 2480 RAND USR 16514
 2490 CLS 
 2500 SLOW 
 2510 PRINT AT 12,0;"PRESS ""STOP"" ON TAPE DECK"
 2520 PRINT AT 14,0;"PRESS ""ENTER"" ON COMPUTER"
 2530 INPUT Z$
 2540 RETURN 
 2550 REM %I%N%P%U%T% %C%H%E%C%K%E%R% %(%Y% %O%R% %N%)% 
 2560 IF INKEY$="" THEN GOTO 2550
 2570 LET I$=INKEY$
 2580 IF I$<>"Y" AND I$<>"N" AND I$<>"M" THEN GOTO 2550
 2590 RETURN 
 2600 SAVE "1016%0"
 2610 GOTO 300

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top