--- title: "V-F Data Acquisition" id: 72220 type: "computer_media" slug: "v-f-data-acquisition" url: "http://localhost/computer_media/v-f-data-acquisition/" markdown_url: "http://localhost/computer_media/v-f-data-acquisition.md" published_at: "2026-10-23T01:07:37+00:00" modified_at: "2026-10-23T01:07:38+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/10/vdaq-hr.png" excerpt: "Logs analog voltages through a voltage-to-frequency converter on the EAR jack, storing up to 2048 samples and plotting, listing or printing them on the TS2040." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "Silicon Mountain Computers" slug: "silicon-mountain-computers" taxonomy: "post_tag" url: "http://localhost/tag/silicon-mountain-computers/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" indiv: - name: "Fred Nachbaur" slug: "fred-nachbaur" taxonomy: "indiv" url: "http://localhost/indiv/fred-nachbaur/" genre: - name: "Science" slug: "science" taxonomy: "genre" url: "http://localhost/type/science/" media_type: "Program" programmers: - name: "Fred Nachbaur" slug: "fred-nachbaur" taxonomy: "indiv" url: "http://localhost/indiv/fred-nachbaur/" download_url: "https://archive.org/download/timex-sinclair-software-archive/V-F%20Data%20Acquisition%281986%29%28F.%20Nachbaur%29%28TS1000%29%28US%29%28Program%29.zip" mediadate: "1985" producer_company: - id: 20332 title: "Silicon Mountain Computers" type: "company" url: "http://localhost/company/silicon-mountain-computers/" images: - url: "http://localhost/wp-content/uploads/2026/10/vdaq-hr.png" - url: "http://localhost/wp-content/uploads/2026/10/vdaq-1.png" - url: "http://localhost/wp-content/uploads/2026/10/vdaq-label.jpg" article_media: - id: 60318 title: "VDAQ – A Data Acquisition Development Program" type: "article" url: "http://localhost/article/vdaq-a-data-acquisition-development-program/" media_type_tags: "Science" --- # V-F Data Acquisition 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](http://localhost/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 `REM` holding 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 `SCROLL`s and `PLOT`s two samples per row and `COPY`s 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, `RAND` stores 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-dimensions `D$`, 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,89 ```