ZX CHART is a multi-function statistical data analysis and graphing program written by James L. Hill in 1985. It accepts up to 18 named parameters and a calculated maximum number of cases, then computes descriptive statistics (high, low, mean, and standard deviation) for each parameter. The program offers seven chart types from a menu: two scattergraph modes (parameter vs. case number, and parameter vs. parameter), a frequency distribution histogram, and a bar graph showing selected cases relative to their parameter averages. Output can be sent directly to a printer via COPY or previewed on screen, and the program and its data can be saved to tape under the user-supplied data ID name.
Program Analysis
Program Structure
The program is organized into clearly separated functional blocks, each beginning at a round line number. Execution starts at line 1 (after the splash screen at line 9090) and flows through data entry, then jumps to the menu at line 2500. The major sections are:
- Lines 1–310: Initialization, dimension setup, and session header data entry (ID, number of parameters, number of cases).
- Lines 510–700: Parameter name entry loop with paged display and correction support.
- Lines 1010–1240: Data value entry loop, paged 10 rows at a time, with
-999as a missing-value sentinel. - Lines 1500–1630: Single-entry modification by parameter and case index.
- Lines 2010–2170: Statistics computation (high, low, mean, standard deviation) stored into array
D(NP,4). - Lines 2500–2630: Main menu, input routing, and “clear and reformat” confirmation.
- Lines 2700–2780: Shared subroutine for auto-copy / screen-preview mode selection.
- Lines 3000–3550: Scattergraph — parameter vs. case number, with axis-drawing subroutine at 3500.
- Lines 4000–4320: Scattergraph — parameter vs. parameter (cross-plot).
- Lines 5000–5230: Bar graph — selected cases vs. their parameter averages.
- Lines 6000–6410: Frequency distribution histogram.
- Lines 9000–9130: Save-to-tape routine and splash/title screen.
Menu Dispatch
The menu at line 2600 uses an arithmetic GOTO expression — a well-known Sinclair BASIC idiom — to branch to the correct section in a single statement:
GOTO 2610*(D$="8")+9000*(D$="7")+3000*(D$="1")+4000*(D$="2")+5000*(D$="4")+6000*(D$="3")+500*(D$="5")+1500*(D$="6")
Each Boolean sub-expression evaluates to 1 (true) or 0 (false), so only one term contributes a non-zero target. If none match, the result is 0, which causes a “line not found” error — there is no default/error path for invalid menu choices.
Array Layout
| Array | Dimensions | Contents |
|---|---|---|
P(NP,NC) | parameters × cases | Raw data values; -999 = missing |
D(NP,4) | parameters × 4 stats | 1=high, 2=low, 3=mean, 4=std dev |
N$(NP,5) | parameters × 5 chars | Parameter name (5-character fixed strings) |
G(ND,2) | divisions × 2 | Frequency count and scaled bar height |
Statistics Computation
Lines 2010–2170 iterate over all parameters and cases. The standard deviation is computed using the computational formula SQR((G*S2 - TM*TM) / (G*G)), where G is the count of valid (non-sentinel) cases, TM is the sum, and S2 is the sum of squares. This is the population standard deviation, not the sample (Bessel-corrected) version. Division is by G², equivalent to SQR(S2/G - (TM/G)²).
Missing Value Handling
The sentinel value -999 is used throughout to mark missing data. Data entry skips blanks (line 1080 uses VAL D$ only when D$<>""), but the array is initialized to zero by DIM, not to -999. This means cases never explicitly entered will be treated as zero rather than missing — a subtle bug, especially if the user advances past entries using the "A" shortcut.
Parameter Entry — Encoded ID Strings
The program uses a compact string encoding for multi-item selection lists: parameters are entered as a string like .03.07.12, and parsed with VAL Y$(Y*3-1 TO Y*3). Each group is exactly 3 characters (dot + 2-digit number), so the loop count is LEN Y$/3. This requires careful zero-padding by the user; there is no input validation on the format.
Display Techniques
Three pre-built strings are used for decorative display lines:
E$— 32 spaces, used to erase areas of the screen withPRINT AT r,c;E$O$— a 64-character row of▄(lower-half block) characters used as a visual separatorU$— a 64-character row of▀(upper-half block) characters used as a visual separator
Parameter names in N$(N) are printed character-by-character vertically on the Y axis (lines 3500–3550) using sequential PRINT AT calls for each of the five character positions.
Bar Graph Scaling (Section 5000)
The bar graph at lines 5000–5230 draws each parameter’s value relative to its mean, with bars scaled to a range of ±GR where GR = MAX(|high - mean|, |low - mean|). The bar is drawn by slicing the pre-built string G$ (a row of block characters preceded by a vertical bar) to a computed length: G$( TO 1+((P(P,M)-(D(P,3)-GR))*24/(GR*2))). This maps the value into a 0–24 character wide bar. If GR=0 (all values equal), it is set to 1 to prevent division by zero.
Frequency Distribution (Section 6000)
The histogram builds array G(ND,2) where column 1 holds raw counts and column 2 holds counts scaled to a maximum bar height of 15 rows. A special case at line 6155 ensures that values exactly at the upper boundary of the last division are counted in the last bin rather than falling through. Range labels are printed at alternating rows (line 6310: LET X=X+(X<21)-2*(X=21)) to avoid label overlap when divisions are narrow.
Save / Reload Behaviour
Line 9040 saves the program (with its live arrays) under the name stored in H$. Line 9045 then corrupts the last character of H$ by subtracting 128 from its character code — this is an artifact of the SAVE operation and leaves H$ in a modified state. On reload, line 9105 checks whether H$ equals "ZX CHART" (the default/unmodified state); if not, the saved data ID is displayed and execution resumes from the menu (GOTO MENU, where MENU=2500) rather than from the start, allowing a dataset to be loaded and immediately analysed.
Notable Anomalies
- Line 1000 is referenced by the parameter name entry loop (
GOTO 1000at line 560) but does not exist; the loop is intended to jump to the data entry section which begins at line 1010. This causes a “line not found” error if the user pressesAin the parameter name screen. - Line 1500 is referenced at line 1630 (
GOTO 1500) but does not exist; section 1510 is the actual start of the modify-entry block, causing a “line not found” error on repeated modifications. - Line 6295 uses
VAL "(D(N,2)+(D-1)*GI)"insideSTR$, which evaluates the expression at runtime via string-to-value conversion — a valid but unusual way to format a computed number as a string for display. - The
COPYcommand (lines 3250, 4280, 5217, 6380) sends the current screen to a compatible printer; this is available only with appropriate hardware attached.
Content
Source Code
1 REM %Z%X% %C%H%A%R%T-(C) 1985 JAMES L. HILL
20 CLEAR
30 LET MENU=2500
50 LET E$=" "
52 LET O$="................................................................"
54 LET U$="''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"
110 CLS
130 PRINT "DATA ENTRY",,U$;"DATA ID? (8 CHARACTERS MAX)"
140 INPUT H$
160 PRINT AT 2,7;"- ";H$;E$;AT 4,0;"NO. OF PARAMETERS? (18 MAX)"
170 INPUT NP
200 LET G=INT (900/NP)
205 IF G>99 THEN LET G=99
210 PRINT AT 4,17;"- ";NP;E$;AT 6,0;;"NO. OF CASES? (";G;" MAX)"
220 INPUT NC
240 PRINT AT 6,12;"- ";NC;E$;AT 21,0;"ABOVE INFORMATION CORRECT? (Y/N)"
260 INPUT D$
270 IF D$="N" THEN GOTO 110
290 DIM P(NP,NC)
300 DIM D(NP,4)
310 DIM N$(NP,5)
510 CLS
520 PRINT "ENTER PARAMETER NAMES-5 CHRS MAX";U$;AT 18,0;O$;"TOUCH <ENTER> TO LEAVE UNCHANGED""M"" = RETURN TO MENU","""A"" = ADVANCE TO DATA ENTRY"
525 LET G=2
530 FOR N=1 TO NP
540 PRINT AT G,0;"PARAMETER ";N;".",N$(N)
550 INPUT D$
555 IF D$="M" THEN GOTO 2500
560 IF D$="A" THEN GOTO 1000
565 IF D$="" THEN GOTO 580
570 LET N$(N)=D$
580 PRINT AT G,16;N$(N)
582 LET G=G+1
584 IF G<>12 AND N<>NP THEN GOTO 700
600 PRINT AT 17,0;"ABOVE INFORMATION CORRECT? (Y/N)"
610 INPUT D$
620 IF D$="N" THEN LET N=N-10
622 IF N<0 THEN LET N=0
625 FOR G=2 TO 17
630 PRINT AT G,0;E$
636 NEXT G
640 LET G=2
700 NEXT N
1010 FOR P=1 TO NP
1020 LET G=4
1030 CLS
1040 PRINT "PARAMETER ";P;". ";N$(P),"ENTER VALUES FOR EACH CASE",U$
1045 PRINT AT 18,0;O$;"TOUCH <ENTER> TO LEAVE UNCHANGED""M"" = RETURN TO MENU","""A"" = ADVANCE TO NEXT PARAMETER"
1050 FOR N=1 TO NC
1060 PRINT AT G,0;"CASE NUMBER ";N;"?",P(P,N)
1070 INPUT D$
1072 IF D$="M" THEN GOTO 2000
1074 IF D$="A" THEN GOTO 1230
1080 IF D$<>"" THEN LET P(P,N)=VAL D$
1090 PRINT AT G,16;P(P,N);E$
1100 LET G=G+1
1110 IF G<>14 AND N<>NC THEN GOTO 1220
1120 PRINT AT 17,0;"ABOVE INFORMATION CORRECT? (Y/N)"
1130 INPUT D$
1140 IF D$="Y" THEN GOTO 1180
1150 IF D$<>"N" THEN GOTO 1130
1160 LET N=N-10
1170 IF N<0 THEN LET N=0
1190 FOR G=4 TO 17
1200 PRINT AT G,0;E$
1210 NEXT G
1215 LET G=4
1220 NEXT N
1230 NEXT P
1240 GOTO 2000
1510 CLS
1520 PRINT "MODIFY SPECIFIC ENTRY",U$;"PARAMETER ID NUMBER?"
1530 INPUT Y$
1540 PRINT AT 2,21;"- ";Y$;AT 6,0;"CASE NUMBER?"
1550 INPUT X$
1560 PRINT AT 6,11;"- ";X$;AT 10,0;"CURRENT VALUE= ";P(VAL Y$,VAL X$);AT 14,0;"ENTER NEW VALUE -OR TOUCH <ENTER> TO LEAVE UNCHANGED"
1570 INPUT D$
1580 IF D$="" THEN GOTO 1600
1590 LET P(VAL Y$,VAL X$)=VAL D$
1600 IF D$<>"" THEN PRINT AT 18,0;"NEW VALUE= ";VAL D$
1605 PRINT AT 21,0;"ADDITIONAL MODIFICATIONS? (Y/N)"
1610 INPUT D$
1620 IF D$="N" THEN GOTO 2000
1630 GOTO 1500
2010 FOR N=1 TO NP
2015 FOR M=1 TO NC
2016 IF P(N,M)<>-999 THEN GOTO 2020
2017 NEXT M
2018 LET HM=0
2019 GOTO 2030
2020 LET HM=P(N,M)
2030 LET LM=HM
2040 LET TM=0
2050 LET S2=0
2055 LET G=0
2060 FOR M=1 TO NC
2070 IF P(N,M)=-999 THEN GOTO 2120
2075 LET G=G+1
2080 IF P(N,M)>HM THEN LET HM=P(N,M)
2090 IF P(N,M)<LM THEN LET LM=P(N,M)
2100 LET TM=TM+P(N,M)
2110 LET S2=S2+P(N,M)*P(N,M)
2120 NEXT M
2130 LET D(N,1)=HM
2140 LET D(N,2)=LM
2150 LET D(N,3)=TM/G
2160 LET D(N,4)=SQR ((G*S2-TM*TM)/(G*G))
2170 NEXT N
2510 CLS
2520 PRINT " MENU- ENTER CHOICE (1 - 7) ";U$;"1. SCATTERGRAPH- PARAMETER VS CASE NUMBER";AT 5,0;"2. SCATTERGRAPH- PARAMETER VS PARAMETER";AT 8,0;"3. FREQUENCY DISTRIBUTION"
2530 PRINT AT 10,0;"4. BAR GRAPH- SELECTED CASE- PARAMETERS RELATIVE TO THEIR RESPECTIVE AVERAGES";AT 14,0;"5. ENTER/VIEW/MODIFY DATA";AT 16,0;"6. MODIFY SPECIFIC ENTRY";AT 18,0;"7. SAVE PROGRAM WITH DATA";AT 20,0;"8. CLEAR AND REFORMAT",O$
2560 INPUT D$
2570 LET D$=D$(1)
2590 CLS
2600 GOTO 2610*(D$="8")+9000*(D$="7")+3000*(D$="1")+4000*(D$="2")+5000*(D$="4")+6000*(D$="3")+500*(D$="5")+1500*(D$="6")
2610 PRINT "CONFIRM",," ""CLEAR AND REFORMAT"" (Y/N)"
2620 INPUT D$
2630 GOTO (D$="Y")+2500*(D$<>"Y")
2700 PRINT AT 12,0;"AUTO-COPY/SCREEN PREVIEW? (C/S)"
2710 INPUT C$
2720 IF C$="C" THEN PRINT AT 12,0;"AUTO-COPY";E$
2730 IF C$="S" THEN PRINT AT 12,0;"SCREEN PREVIEW";E$
2740 PRINT AT 21,0;"ABOVE INFORMATION CORRECT? (Y/N)"
2750 INPUT G$
2760 IF G$="N" THEN GOTO 2590
2770 CLS
2780 RETURN
3010 PRINT "ID NUMBERS OF PARAMETERS TO BE GRAPHED?",,"(ENTER AS "".NN.NN.ETC"" OR ""ALL"")"
3020 INPUT Y$
3022 PRINT AT 1,7;"-";E$;AT 2,0;Y$;E$
3028 GOSUB 2700
3030 FOR Y=1 TO NP*(Y$="ALL")+LEN Y$/3*(Y$<>"ALL")
3050 IF Y$="ALL" THEN LET N=Y
3060 IF Y$<>"ALL" THEN LET N=VAL Y$(Y*3-1 TO Y*3)
3070 CLS
3080 PRINT "%P%A%R%A%M% %V%S% %C%A%S%E DATA ID """;H$;"""";AT 1,0;"MEAN= ";D(N,3);AT 1,16;"S.D.= ";D(N,4);AT 2,0;"RANGE= ";D(N,2);" TO ";D(N,1)
3085 PRINT AT 21,5;"NUMBER OF CASES= ";NC
3090 GOSUB 3500
3140 LET YI=31/((D(N,1)-D(N,2))+(D(N,1)=D(N,2)))
3150 LET XI=57/(NC-1)
3160 FOR M=1 TO NC
3170 IF P(N,M)=-999 THEN GOTO 3190
3180 PLOT (M-1)*XI+4,(P(N,M)-D(N,2))*YI+4
3190 NEXT M
3200 IF C$="C" THEN GOTO 3250
3210 INPUT D$
3220 IF D$="C" THEN GOTO 3250
3230 IF D$="M" THEN GOTO 2500
3240 GOTO 3260
3250 COPY
3260 CLS
3270 NEXT Y
3280 GOTO 2500
3500 PRINT AT 4,0;"P";AT 5,0;"A";AT 6,0;"R";AT 7,0;"A";AT 8,0;"M";AT 10,0;INT (N/10);AT 11,0;N-(INT (N/10)*10);AT 13,0;N$(N,1);AT 14,0;N$(N,2);AT 15,0;N$(N,3);AT 16,0;N$(N,4);AT 17,0;N$(N,5)
3510 PRINT AT 20,1;" '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' "
3520 FOR G=4 TO 19
3530 PRINT AT G,1;" :"
3540 NEXT G
3550 RETURN
4010 PRINT "ID NUMBERS OF PARAMETERS TO BE PLACED ON Y AXIS?","(ENTER AS "".NN.NN.ETC"" OR ""ALL"")"
4020 INPUT Y$
4030 PRINT AT 1,16;"- ";E$;AT 2,0;Y$;E$;AT 4,0;"ID NUMBERS OF PARAMETERS TO BE PLACED ON X AXIS?"
4040 INPUT X$
4045 PRINT AT 5,16;"- ";E$;AT 6,0;X$;E$
4046 GOSUB 2700
4050 FOR Y=1 TO NP*(Y$="ALL")+LEN Y$/3*(Y$<>"ALL")
4060 IF Y$="ALL" THEN LET N=Y
4080 IF Y$<>"ALL" THEN LET N=VAL Y$(Y*3-1 TO Y*3)
4090 LET YI=31/((D(N,1)-D(N,2))+(D(N,1)=D(N,2)))
4100 FOR X=1 TO NP*(X$="ALL")+LEN X$/3*(X$<>"ALL")
4120 IF X$="ALL" THEN LET M=X
4130 IF X$<>"ALL" THEN LET M=VAL X$(X*3-1 TO X*3)
4140 LET XI=57/((D(M,1)-D(M,2))+(D(M,1)=D(M,2)))
4150 PRINT "%P%A%R%A%M% %V%S% %P%A%R%A%M DATA ID ";H$;AT 1,0;"RANGE PARAM ";N;"= ";D(N,2);" TO ";D(N,1);AT 2,0;"RANGE PARAM ";M;"= ";D(M,2);" TO ";D(M,1);AT 21,4;"PARAM ";M;" ";N$(M)
4160 GOSUB 3500
4190 FOR Q=1 TO NC
4200 IF P(N,Q)=-999 OR P(M,Q)=-999 THEN GOTO 4220
4210 PLOT (P(M,Q)-D(M,2))*XI+4,(P(N,Q)-D(N,2))*YI+4
4220 NEXT Q
4230 IF C$="C" THEN GOTO 4280
4240 INPUT D$
4250 IF D$="C" THEN GOTO 4280
4260 IF D$="M" THEN GOTO 2500
4270 GOTO 4290
4280 COPY
4290 CLS
4300 NEXT X
4310 NEXT Y
4320 GOTO 2500
5002 PRINT "ID NUMBERS OF PARAMETERS TO BE GRAPHED?",,"(ENTER AS "".NN.NM.ETC"" OR ""ALL"")"
5003 INPUT Y$
5005 PRINT AT 1,7;"- ";E$;AT 2,0;Y$;E$;AT 4,0;"CASES TO BE GRAPHED?"
5006 INPUT X$
5007 PRINT AT 4,19;"- ";E$;AT 5,0;X$;E$
5008 GOSUB 2700
5018 LET G$="...................................................."
5020 IF X$="ALL" THEN FOR Q=1 TO NC
5025 IF X$<>"ALL" THEN FOR Q=1 TO LEN X$/3
5030 IF X$="ALL" THEN LET M=Q
5035 IF X$<>"ALL" THEN LET M=VAL X$(Q*3-1 TO Q*3)
5080 CLS
5090 PRINT "%P%A%R%A%M% %V%S% %A%V%E DATA ID """;H$;"""";AT 1,0;"CASE NO. ";M
5100 FOR N=3 TO 21
5110 PRINT AT N,5;" :";AT N,18;"."
5120 NEXT N
5122 PRINT AT 21,6;"%M%I%N................%A%V%E................%M%A%X"
5125 IF Y$="ALL" THEN FOR N=1 TO NP
5130 IF Y$<>"ALL" THEN FOR N=1 TO LEN Y$/3
5133 IF Y$="ALL" THEN LET P=N
5136 IF Y$<>"ALL" THEN LET P=VAL Y$(N*3-1 TO N*3)
5140 IF P(P,M)=-999 THEN GOTO 5190
5150 LET A=ABS (D(P,1)-D(P,3))
5160 LET B=ABS (D(P,2)-D(P,3))
5170 LET GR=A*(A>=B)+B*(B>A)
5175 IF GR=0 THEN LET GR=1
5180 PRINT AT P+2,0;N$(P);" :";G$( TO 1+((P(P,M)-(D(P,3)-GR))*24/(GR*2)))
5190 NEXT N
5210 IF C$="C" THEN GOTO 5217
5212 INPUT D$
5214 IF D$="C" THEN GOTO 5217
5215 IF D$="M" THEN GOTO 2500
5216 GOTO 5218
5217 COPY
5218 CLS
5220 NEXT Q
5230 GOTO 2500
6010 PRINT "ID NUMBERS OF PARAMETERS TO BE GRAPHED?",,"(ENTER AS "".NN.NN.ETC"" OR ""ALL"")"
6020 INPUT Y$
6030 PRINT AT 1,7;"- ";E$;AT 2,0;Y$;E$;AT 4,0;"NUMBER OF DIVISIONS? (2 - 12)"
6040 INPUT ND
6050 PRINT AT 4,19;"- ";ND;E$
6060 GOSUB 2700
6065 LET G$=" :% % % % % % % % % % % "
6070 FOR Y=1 TO NP*(Y$="ALL")+LEN Y$/3*(Y$<>"ALL")
6080 IF Y$="ALL" THEN LET N=Y
6090 IF Y$<>"ALL" THEN LET N=VAL Y$(Y*3-1 TO Y*3)
6100 LET T=0
6110 LET GI=(D(N,1)-D(N,2))/ND
6120 DIM G(ND,2)
6130 FOR M=1 TO NC
6135 IF P(N,M)=-999 THEN GOTO 6180
6140 FOR D=1 TO ND
6150 IF P(N,M)>=D(N,2)+(D-1)*GI AND P(N,M)<D(N,2)+D*GI THEN LET G(D,1)=G(D,1)+1
6155 IF D=ND AND P(N,M)=D(N,2)+D*GI THEN LET G(D,1)=G(D,1)+1
6160 IF G(D,1)>T THEN LET T=G(D,1)
6170 NEXT D
6180 NEXT M
6190 FOR D=1 TO ND
6200 LET G(D,2)=G(D,1)*15/T
6210 NEXT D
6212 PRINT "%F%R%E%Q% %D%I%S%T DATA ID """;H$;"""";AT 1,0;"PARAMETER NO. ";N;"- ";N$(N);AT 2,0;"%F%R%E%Q";AT 21,0;"%R%A%N%G%E";AT 18,3;" '''''''''''''''''''''''''''''''''''''''''''''''''"
6214 FOR D=3 TO 17
6215 PRINT AT D,3;" :........................"
6216 NEXT D
6220 LET X=19
6230 FOR D=1 TO ND
6240 LET L=17
6250 FOR M=1 TO G(D,2)
6260 PRINT AT L,(D-1)*INT (24/ND)+4;G$( TO INT (24/ND))
6270 LET L=L-1
6280 NEXT M
6290 PRINT AT L+1,0;G(D,1)
6295 LET X$=STR$ VAL "(D(N,2)+(D-1)*GI)"
6296 IF LEN X$>5 THEN LET X$=X$( TO 5)
6300 PRINT AT X,(D-1)*INT (24/ND)+4;X$
6310 LET X=X+(X<21)-2*(X=21)
6320 NEXT D
6330 IF C$="C" THEN GOTO 6380
6340 INPUT D$
6350 IF D$="M" THEN GOTO 2500
6360 IF D$="C" THEN GOTO 6380
6370 GOTO 6390
6380 COPY
6390 CLS
6400 NEXT Y
6410 GOTO 2500
9000 REM %S%A%V%E
9010 CLS
9020 PRINT "PROGRAM AND DATA WILL BE SAVED UNDER THE NAME """;H$;"""";AT 4,0;"START RECORDER- THEN TOUCH ENTER"
9030 INPUT D$
9040 SAVE H$
9045 LET H$(LEN H$)=CHR$ (CODE H$(LEN H$)-128)
9090 CLS
9100 PRINT AT 5,3;"% ";AT 6,3;"% %Z%X% %C%H%A%R%T";AT 7,3;"% ################################";AT 8,3;"% %(%C%)% %1%9%8%5% %B%Y% %J%A%M%E%S% %L%.% %H%I%L%L";AT 9,3;"% ##########################";AT 10,3;"% %T%O%U%C%H% %<%E%N%T%E%R%>% %T%O% %B%E%G%I%N";AT 11,3;"% ################################################";AT 12,3;U$( TO 26)
9105 IF H$<>"ZX CHART" THEN PRINT AT 20,0;"PROGRAM CURRENTLY CONTAINS DATA IDENTIFIED AS """;H$;""""
9110 INPUT D$
9120 IF H$="ZX CHART" THEN GOTO 1
9130 GOTO MENU
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

