Turns a TS1000 or ZX81 into a data logger by reading an analog voltage through a voltage-to-frequency (V-F) converter wired to the EAR jack. Machine code counts the pulses in a timed window, and the count is proportional to the input voltage. A 4096-byte string holds 32 “screens” of 64 two-byte samples, with sample rates from about 3 ms to 1 second. Faster rates give 8-bit resolution and the slowest give about 14-bit. The menu acquires one screen or a run of several screens, plots a screen, prints the values in two columns, resets the plot limits, recalibrates, and draws a long “side-winder” strip chart on the TS2040 printer. Acquisition can start on a keypress, or when the input frequency becomes non-zero or drops to zero.
The program is Fred Nachbaur’s “V-F Data Acquisition 1”. His article “VDAQ – A Data Acquisition Development Program” covers the theory, an LM331 V-F circuit, use with the VOTEM analog interface and the program itself. The REMs and title screen give program 1, version 1.3, ©1984. The archive’s file name gives 1986. The zip also has a hi-res version, VDAQ1-HR, version 1.40, dated 1985.
Program Analysis
Program Structure
- Line 0: a 407-byte
REMholding the machine code. Lines 4–7 reset the automatic-listing pointers so line 0 can’t jam the listing. - Lines 500–620: the menu.
GOTO N2*H5*VAL Z$jumps to 1000 × the option number. - Lines 1000–1290: acquire a screen. The time base is converted into a 16-bit loop count and the acquisition routine runs at
USR 16594. Key start, non-zero start and zero start are handled at 1220 and 1260. - Lines 2000–2160: plot a screen. Machine code at 16636 draws the axes and the routine at 16776 plots the points, scaled between the plot limits.
- Lines 3000–3190: print the 64 values of a screen as time in ms and voltage, in two columns.
- Lines 4000–4090: set the minimum and maximum plot voltages.
- Lines 5000–5450: initialize. Sets the constants and
DIM D$(4096), then calibrates the voltage against a measured reference and the time base against a stopwatch. - Lines 6000–6070: multi-screen acquisition. Lines 7000–7380: the side-winder plot, which
SCROLLs andPLOTs two samples per row andCOPYs every 22 rows, with a dry-run option that shows it on screen only. - Lines 7900–8460: subroutines: choose a screen, choose a count, a two-digit input routine that needs no ENTER, store and fetch the per-screen timing values, and a COPY/continue prompt.
- Lines 9970–9999: save and stop.
Hi-Res Version
VDAQ1-HR (version 1.40, ©1985) moves the machine code up to around 25000 and adds hi-res routines (HRM and LRM) that switch between a hi-res graph and the normal display. It plots 1, 2 or 4 files across the full screen, prints labels into the hi-res screen one character at a time through the routine at lines 4–9, has a reverse-video option, and uses its own COPY routine. Data is organized into 16 files instead of 32, and the side-winder plot is gone. Its initialization doesn’t set the constants, which are kept in the saved variables. A REM warns “DONT USE RUN” and says to restart with GOTO 500.
Notable Techniques
- The voltage comes in through the EAR jack as a pulse train, so the only hardware needed besides the V-F converter is a single input bit.
- Small constants (
N0,N1,N2,O0,TFS,H5) and subroutine addresses (FI,FS,CO,AQ) are kept in variables to save memory. - To split a number into two bytes for POKEing into the machine code,
RANDstores it in SEED and the two bytes are read back from 16434/16435. This doesn’t work for zero, so the low plot limit is split arithmetically at lines 2120–2130. - Line 1150 picks the start mode with one computed jump:
GOTO 1140+(80 AND Z$="K")+(120 AND (Z$="N" OR Z$="Z")). - Line 9999 POKEs 16384 so that the program ends without a report code. The article credits this trick to SYNAPSE.
- The routine at 8000 takes a two-digit screen number without ENTER, or one digit followed by ENTER.
Potential Issues
- Menu option 0 passes the range check at line 600 and runs
GOTO 0, which drops into the initialization at line 5000 and re-dimensionsD$, losing any acquired data. - In the hi-res version, line 4090 reads
GOTO VAL "400£", which can’t be evaluated. It was probably meant to be 4000.
Source Code
0 REM [D][A][T][A]█[A][Q][.][F][N][1][9][8][4] V 1.3 ▘ ) COPY COPY.#[N]C><= RETURN▛K PLOT ▀.#[N]C▛<= RETURN▛S PLOT / DIM TAN YRNDEEXP INKEY$ 6EXP INKEY$ MSQR INKEY$ LN [/]RNDEEXP INKEY$ #7#7USQR INKEY$ XCOS / DIM USGN INKEY$ XCOS MSGN INKEY$ LN [Q]RND6EXP INKEY$ / LIST Y█E(RND)▞ ; GOSUB #ATN INKEY$ ",,XC▝/ IF 6EXP INKEY$ TAN )5 E£RND7Y=Q.;XC▝/ SAVE Y4Q.7XCOS / RAND :▘#[E] NEW█#STR$ LN #INKEY$ STOPLN #INKEY$ FOR LPRINT #[B]CQ#[L] FAST GOSUB # GOSUB # FASTAT SGN 5 RZ PRINT ##2▒DK▌[?] GOSUB #/,,▗ GOSUB #S▀ GOSUB PIXW14 GOTO # LET K LLIST ACS #4▘ FOR ACS #COS /"LN #INKEY$ ACS (SQR /▀#▛SQR FOR [B] GOSUB # GOSUB #TAN VAL ▞(##▌: DACS )*K▘;( RUN AT TAN Y MSQR INKEY$ EEXP INKEY$ - #;;#7#VAL LPRINT GOSUB #USR INKEY$ GOSUB #S3 GOSUB #ABS INKEY$ LN *INKEY$ 77# RETURN S?# RETURNGK~~#USQR INKEY$ #Y█LN [M]"USQR INKEY$ W RETURNRNDCOS /[Z] £ [£]#Z▘/ #D THEN#5▖5▖5▖5▖5▖5▖5▖5▖[X] RUN COPY COPYLN LN LN LN LN LN LN LN LN LN LN LN LN LN LN LN LN LN LN LN
4 POKE 16394,8
5 POKE 16395,0
6 POKE 16419,8
7 POKE 16420,0
8 REM [V][-][F]█[D][A][T][A]█[A][C][Q][U][I][S][I][T][I][O][N]█[1]
9 REM BY F.NACHBAUR 1984
10 GOTO 5000
12 REM
15 REM [G][O][T][O]█[5][0][0]█[T][O]█[R][E][S][T][A][R][T]
500 CLS
510 POKE BL,N0
520 SLOW
530 PRINT TAB 6;"[V][-][F]█[D][A][T][A]█[A][C][Q][U][I][S][I][T][I][O][N]",,,TAB 6;"PROGRAM 1, V 1.3";TAB 6;"SAMPLE RATE 3-1000 MSEC.",,,L$;TAB 3;"(C)1984","BY F.NACHBAUR",L$
540 PRINT ,,"[C][H][O][O][S][E]█[A][N]█[O][P][T][I][O][N][:]",,,"1. ACQUIRE DATA (SINGLE SCREEN)","2. PLOT DATA",,"3. PRINT DATA",,L$;"4. RE-SET PLOT LIMITS","5. INITIALIZE",,L$;"6. MULTI-SCREEN ACQUISITION","7. SIDE-WINDER PLOT (TS2040)",L$,,"A. STOP","S. SAVE "
550 IF INKEY$ ="" THEN GOTO 550
560 LET Z$=INKEY$
570 POKE BL,N2
580 IF Z$="S" THEN GOTO 9970
590 IF Z$="A" THEN GOTO 9990
600 IF Z$>"7" OR Z$<"0" THEN GOTO H5
610 CLS
620 GOTO N2*H5*VAL Z$
1000 REM [A][C][Q][U][I][R][E]█[D][A][T][A]
1010 LET N=N1
1020 GOSUB FI
1030 POKE 16849,N+N1
1040 PRINT "ENTER TIME BASE (.1-";TREF;" SEC): ";
1050 INPUT TB
1060 IF TB<.1 OR TB>TREF THEN GOTO 1050
1070 LET C0=(65535*TB/TREF)
1080 RAND C0
1090 POKE 16540,PEEK S1
1100 POKE 16541,PEEK S2
1110 GOSUB FS
1120 PRINT TB
1130 PRINT ,,"START BY [K]EY OR [N]ON-[Z]ERO INPUT ?"
1140 LET Z$=INKEY$
1150 GOTO 1140+(80 AND Z$="K")+(120 AND (Z$="N" OR Z$="Z"))
1160 FAST
1170 RAND USR AQ
1180 SLOW
1190 PRINT AT 21,N0;"==[D][A][T][A]██[A][C][Q][U][I][R][E][D]=="
1200 GOSUB CO
1210 GOTO H5
1220 PRINT AT 21,N0;"[A][N][Y]█[K][E][Y]█[T][O]█[A][C][Q][U][I][R][E]"
1230 IF INKEY$ <>"" THEN GOTO 1230
1240 IF INKEY$ ="" THEN GOTO 1240
1250 GOTO 1160
1260 PRINT AT 21,N0;"CHECKING..."
1270 LET C=USR 16536
1280 IF (C AND Z$="N") OR (NOT C AND Z$="Z") THEN GOTO 1160
1290 GOTO 1270
2000 REM [P][L][O][T]
2010 CLS
2020 GOSUB FI
2030 GOSUB FR
2040 LET R=INT ((CH-CL)/40)
2050 CLS
2060 PRINT (" "+STR$ PH+" V ---------------------------")( TO 32)
2070 RAND USR 16636
2075 POKE BL,N0
2080 PRINT AT O0,N0;"*";AT 21,N0;PL;TAB O0;"<---TIME--->";TAB 27;TB;TAB 14;"[F][I][L][E]:";FL
2085 POKE BL,N2
2090 RAND R
2100 POKE 16850,PEEK S1
2110 POKE 16851,PEEK S2
2120 POKE 16853,INT (CL/TFS)
2130 POKE 16852,CL-TFS*PEEK 16853
2140 RAND USR 16776
2150 GOSUB CO
2160 GOTO H5
3000 REM [P][R][I][N][T]
3010 GOSUB FI
3020 FAST
3030 GOSUB FR
3040 CLS
3050 PRINT ,,"FILE NO:",FL,"TIME BASE =",TB;" SEC.","SAMPLE RATE = ";SR;" MSEC",L$,,"[N][O]█[M][S][E][C]█[V][O][L][T][A][G][E]██[N][O]█[M][S][E][C]█[V][O][L][T][A][G][E]",,
3060 LET DP=O0**INT (.2+LN CM/LN O0)
3070 FOR A=N1 TO 32
3080 FAST
3090 LET D=N2*(A-N1)+PEEK 16846+TFS*PEEK 16847
3100 LET VL=(PEEK D+TFS*PEEK (D+1))/CM
3110 LET VL=(INT (VL*DP))/DP
3120 LET VH=(PEEK (D+64)+TFS*PEEK (D+65))/CM
3130 LET VH=(INT (VH*DP))/DP
3140 PRINT A;TAB 3;INT (A*SR+N1/N2);TAB 9;VL;TAB 16;"▌";A+32;TAB 20;INT ((A+32)*SR+N1/N2);TAB 26;VH
3150 IF A=14 THEN GOSUB CO
3160 IF A=14 THEN CLS
3170 NEXT A
3180 GOSUB CO
3190 GOTO H5
4000 PRINT "PRESENT LIMITS:",,"MIN. VOLTAGE=";PL,"MAX. VOLTAGE=";PH
4010 PRINT ,,"CHANGE TO:",,"MIN.=";
4020 INPUT PL
4030 PRINT PL,,"MAX.=";
4040 INPUT PH
4050 PRINT PH,,"OK?"
4060 IF INKEY$ ="" THEN GOTO 4060
4070 IF INKEY$ ="Y" THEN GOTO H5
4080 CLS
4090 GOTO 4000
5000 PRINT "[I][N][I][T][I][A][L][I][Z][E][:]"
5010 DIM D$(4096)
5020 LET L$="▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"
5030 LET N0=0
5040 LET N1=1
5050 LET N2=2
5060 LET O0=10
5070 LET TFS=256
5080 LET S1=16434
5090 LET S2=16435
5100 LET H5=500
5110 LET BL=16418
5120 LET FI=7900
5130 LET SS=7950
5140 LET FS=8200
5150 LET FR=8300
5160 LET CO=8400
5170 LET AQ=16594
5180 LET PL=N0
5190 LET PH=N1
5200 LET N=N1
5210 PRINT "SET SW. 3,6,7; MEASURE VREF",,,"VREF=";
5220 INPUT VREF
5230 PRINT VREF
5240 POKE 16540,255
5250 POKE 16541,255
5260 FAST
5270 LET C=USR 16536
5280 SLOW
5290 PRINT ,," MAX COUNT=";C
5300 LET C1=C/VREF
5310 PRINT "1V COUNT AT MAX TIME=";C1;" "
5320 PRINT ,,"SET TIME-BASE; PRESS ENTER WHEN READY, NOTE TIME WHEN DISPLAY RE-APPEARS."
5330 IF INKEY$ <>CHR$ 118 THEN GOTO 5330
5340 FAST
5350 LET FL=N1
5360 GOSUB 7915
5370 LET C0=65535
5380 GOSUB FS
5390 PRINT "LAST COUNT=";USR 16566
5400 SLOW
5410 PRINT ,,"TIME=?";
5420 INPUT TREF
5430 PRINT TREF
5440 GOSUB CO
5450 GOTO H5
6000 REM [M][U][L][T][I][-][A][Q]
6010 CLS
6020 PRINT "START AT "
6030 GOSUB FI
6040 PRINT
6050 GOSUB SS
6055 IF FL+N>33 THEN GOTO 6000
6060 PRINT ,,,,"(EACH SCREEN)="
6070 GOTO 1030
7000 REM [S][I][D][E][-][P][L][O][T]
7010 GOSUB FI
7020 GOSUB SS
7025 IF FL+N>33 THEN GOTO 7000
7030 GOSUB FR
7040 LET R=((CH-CL)/60)
7050 LET DR=N0
7060 PRINT ,,"DRY RUN?"
7070 IF INKEY$ <>"" THEN GOTO 7070
7080 IF INKEY$ ="" THEN GOTO 7080
7090 IF INKEY$ ="Y" THEN LET DR=N1
7100 FAST
7110 IF NOT DR THEN LPRINT ,,"TB=";TB;" SEC PER SCREEN","SAMPLE RATE=";SR;" MSEC.","TOTAL TIME=";N*TB;" SEC"
7120 IF NOT DR THEN LPRINT ,,STR$ PL;"=MIN. VOLTS";TAB 21;"MAX.=";PH
7130 LET D=PEEK 16846+TFS*PEEK 16847
7140 LET F=FL
7150 LET C=N0
7160 LET L=N0
7170 LET TM=N0
7180 FOR L=L TO L+21
7190 SCROLL
7200 IF L>=32*N THEN GOTO 7340
7210 PRINT AT 21,30;".";CHR$ (O0*NOT TM);AT 21,N0;"T";AT 21,N0;(CHR$ (F+156)+"----.----+----.----+----.----I") AND NOT C
7220 FOR Y=N1 TO N0 STEP -N1
7230 LET B=PEEK D+TFS*PEEK (D+N1)
7240 LET X=INT ((B-CL)/R+N1/N2)
7250 IF X<64 AND X>=N0 THEN PLOT X,Y
7260 LET D=D+N2
7270 NEXT Y
7280 LET TM=TM+N1
7290 LET C=C+N1
7300 IF TM=5 THEN LET TM=N0
7310 IF C<>32 THEN GOTO 7340
7320 LET C=N0
7330 LET F=F+N1
7340 NEXT L
7350 IF NOT DR THEN COPY
7360 IF DR THEN PAUSE 4E4
7370 IF L<32*N THEN GOTO 7180
7380 GOTO H5
7900 LET N$="WHICH SCREEN? (01-32)= "
7905 GOSUB 8000
7910 LET FL=A
7915 POKE 16844,FL
7920 POKE 16845,N0
7925 RAND USR 16611
7930 RETURN
7950 LET N$="HOW MANY SCREENS? ( TO "+STR$ (33-FL)+"): "
7960 GOSUB 8000
7970 LET N=A
7980 RETURN
8000 PRINT ,,N$;
8010 LET Z$=INKEY$
8020 IF Z$<"0" OR Z$>"9" THEN GOTO 8010
8030 PRINT Z$;
8040 IF INKEY$ <>"" THEN GOTO 8040
8050 LET Y$=INKEY$
8060 IF Y$="" THEN GOTO 8050
8070 IF CODE Y$=118 THEN GOTO 8110
8080 PRINT Y$;
8090 IF Y$<"0" OR Z$>"3" OR (Z$="3" AND Y$>"2") OR Y$>"9" THEN GOTO 8160
8100 GOTO 8130
8110 LET Y$=Z$
8120 LET Z$="0"
8130 PRINT " *"
8140 LET A=O0*VAL Z$+VAL Y$
8150 RETURN
8160 CLS
8170 GOTO 8000
8200 RAND C0
8210 FOR F=FL TO FL+N-1
8220 POKE 16854+2*F,PEEK S1
8230 POKE 16855+2*F,PEEK S2
8240 NEXT F
8250 RETURN
8300 LET C0=PEEK (16854+N2*FL)+TFS*PEEK (16855+N2*FL)
8310 LET CM=C1*C0/65535
8320 LET TB=INT (TREF*C0/655.35+.5)/O0/O0
8330 LET SR=N2*H5*TB/64
8340 LET CL=PL*CM
8350 LET CH=PH*CM
8360 RETURN
8400 POKE 16418,N0
8410 SLOW
8420 PRINT AT 23,N0;"[Z]- COPY";TAB 23;"[C]- CONT "
8430 POKE 16418,N2
8440 IF INKEY$ ="Z" THEN COPY
8450 IF INKEY$ ="C" THEN RETURN
8460 GOTO 8440
9970 CLS
9974 PRINT "█[P][U][T]█[R][E][C][O][R][D][E][R]█[I][N]█["][R][E][C][O][R][D]["]█[M][O][D][E]█",,,"██[T][H][E][N]█[P][R][E][S][S]█[A][N][Y]█[K][E][Y]█[T][O]█[S][A][V][E][.]██"
9976 IF INKEY$ <>"" THEN GOTO 9976
9978 IF INKEY$ ="" THEN GOTO 9978
9980 CLS
9982 SAVE "VDAQ[1]"
9984 GOTO H5
9985 SAVE "VDAQ[1]"
9986 RUN
9990 CLS
9991 POKE BL,0
9992 PRINT AT 21,0;" GOTO 500 TO RE-START","""ENTER"" TO LIST "
9993 FAST
9999 POKE 16384,89Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


