Classic Computer Magazine Archive COMPUTE! ISSUE 21 / FEBRUARY 1982 / PAGE 160

Measure Time Intervals With The Pet Parallel User Port

Robert Macnaughton
Rexdale, Canada

This article describes a machine language program that can be used to measure seven successive small time intervals, using the CBM Parallel User Port (PUP), and eight phototransistors, to the nearest 1/10000s.

Since no page zero locations are used, this program should run on any PET (except 4.0, since it would need to be moved above 864 decimal for 4.0 BASIC).

The PUP, located at the back of the CBM, consists of 24 contacts to the main logic board, labelled as follows:

Only the bottom row of contacts will be used. The top row of contacts are for use by CBM diagnostic routines during servicing.

On the bottom row of contacts, Pin M is the CB2 line, used in many programs for sound effects; contacts A and N are grounds, and contact B is the CA1 line.

We will use contacts C,D,E,F,H,J,K and L, known as PA0, PA1, PA2, PA3, PA4, PA5, PA6 and PA7, the programmable input/output lines, to receive information from eight phototransistors, the detectors of the position of some moving object.

The eight lines are treated by the PET as a single memory location, 59471 in decimal or $E84F in hexadecimal. It is known as the ORA, the output register for I/O Port A, without handshaking. At any time, a PEEK(59471) will indicate the condition of the ORA.

The DDR A, the data direction register for Port A, is used to designate which are the input and which are the output lines of the ORA. Its address is 59459 or $E843. A zero in bit three would make PA3 an input line and a one would make it an output line. If you POKE 59459,76 then PA2, PA3 and PA6 will be output lines and the rest input lines, since 76 in binary is 01001100.

In this timer, all eight lines are made inputs by POKE 59459,0. A PEEK(59459) when the CBM is first turned on will show that all the lines are initially inputs.

When running, the timer program looks at the contents of the ORA again and again. To understand the result, the contents of 59471 must be expressed as a binary number. Each of the eight I/O lines corresponds to one bit in this number. Any line grounded will be represented as a 0. If not grounded, it will be represented as a 1. More exactly, if a resistance of less than about 2000Ω is connected from a PA line to GND, the state of the line will be interpreted as a 0. If the resistance is more than 2000Ω, it will be interpreted as a 1.

If you PEEK(59471) with nothing connected to the PUP, you will get 255. If you short out all eight lines, you will get a 0. (First make sure that they are all input lines.)

PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0
bit 7 6 5 4 3 2 1 0
value 128 64 32 16 8 4 2 1
59471
255 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1
2 0 0 0 0 0 0 1 0
4 0 0 0 0 0 1 0 0
8 0 0 0 0 1 0 0 0
16 0 0 0 1 0 0 0 0
32 0 0 1 0 0 0 0 0
64 0 1 0 0 0 0 0 0
128 1 0 0 0 0 0 0 0
214 1 1 0 1 0 1 1 0

The collectors of eight FPT100 phototransistors are connected to the eight PA lines, and their emitters to ground at contact N. When enough light strikes a phototransistor such as the FPT100, its resistance falls to about 200Ω. This is interpreted as a 0 in the ORA. When the light is cut off, the resistance increases dramatically and is interpreted as a 1. As an object passes by a phototransistor, the state of that PA line will change from 0 to 1 and back to 0 as the light is temporarily interrupted.

I have placed the phototransistors in holes drilled in a meter stick 15 cm apart. The position of the first phototransistor must be adjustable to start the timer at the correct moment. Opposite each phototransistor is a small flashlight bulb attached to a second meter stick. The two meter sticks are placed on either side of a ramp. A large ball bearing rolling down the ramp will be timed as it interrupts each light beam in turn.

If the times you wished to measure were long, you could write a BASIC program to measure these time intervals, using the internal "jiffy" clock of the PET. The light to each phototransistor would have to be cut off long enough that it would still be cut off when the program got around to checking the state of 59471.

To fully utilize the 1 megacycle clock in the CBM, a machine language program must be used.

The program begins by setting the interrupt flag. This will ensure that the timmig will not be interrupted by the CBM as it performs its normal interrupt every 1/60 s, to update its clock, flash the cursor if needed, etc.

It then goes into a loop to load all the various memory locations used to store the times, with zeros. At the same time it prints a ? at the top left of the screen. It then goes into a second loop to wait for PA? to become 1 when the ball is rolled into place at the top of the ramp. An R for READY now appears on the screen.

The following table shows how the ORA changes as the ball rolls down the ramp.

SCREEN BINARY DECIMAL
? 00000000 0 ball not on ramp
R 00000001 1 ball in place at top of ramp
T 00000000 0 ball rolling
1 00000010 2 passes PA1
1 00000000 0 ball rolling
2 00000100 4 passes PA2
2 00000000 0 ball rolling
3 00001000 8 passes PA3
3 00000000 0 ball rolling
4 00010000 16 passes PA4
4 00000000 0 ball rolling
5 00100000 32 passes PA5
5 00000000 0 ball rolling
6 01000000 64 passes PA6
6 00000000 0 ball rolling
7 10000000 128 passes PA6

When 59471 becomes 0, the timer enters a timing loop. Each time through the loop it checks 59471 for a 0, then adds 1 to a counter. When 59471 has the next expected value, the contents of this counter are stored, and the timing resumes, continuing until all seven times have been measured. When the program returns to BASIC, the contents of the memory locations containing the count can be recalled and converted to seconds.

Since each timing loop takes 43 cycles of the CBM's internal 1 megacycle clock, each count represents 43 microseconds.

The count is contained in three locations. The first is incremented in each loop. The second is incremented only when the first passes 255 and becomes 0 again. The third is incremented only when the second passes 255 and becomes 0 again. The largest count possible is then (255×256×256) + (255×256) + 255 or 16777215. This is slightly more than 12 minutes.

I have included a second copy of the machine language program which shows the timing loop. Beside each step I have written the number of cycles of the PET's internal clock that are needed to complete each step. The total number of cycles is 43. Some extra time is used to store the count as each phototransistor is passed. If you wish, this could be calculated and added on to the total time as a correction.

I have also included a BASIC program to operate the clock in an organized fashion. It asks you how many runs you wish to make down the ramp, then stores the seven times for each run. Eventually, the average time for each part of the run is calculated. With a few minor changes, this program can be used in almost any situation where accurate timing is needed.

TIMER COMMENTS
1 Disable the interrupt flag
2 Load the accum with the code for ?
3 Store at top left corner of screen
4 Load the x-register with a 2
5 Store the 2 at 0336
6 Load the y-register with decimal 25
7 Load the accum with a 0
8 Store 0 at all locations from 03DF to 03DF + 25 by looping until y = 0
10 Compare y with zero
11 12 If y isn't zero then loop to step 7
12 Load accum with the contents of 59471
13 Check if PA0 is a 1 or a 0
14 If PA0 = 0 then loop and check again
15 Now PA0 is 1: R for Ready into accum
16 store R on the screen
17 This is a time delay while things
18 settle down. Load x and y with 255
19 and decrement them both to zero
20 Each time x decrements from 255 to 0
21 y decrements by one. Finally both are
22 zero
23 Load accum with 59471 once more
24 Test to see if PA0 is still a 1
25 If so, loop back to 23 and try again
26 Now PA0 is a 0, the timing must start
27 Store a T on the screen
28 Begin timing loop by clearing the
29 carry flag, then load accum with 03E0
32 Add 1 to the contents of this
30 location and then store it back there
31 Add zero to the contents of 03E1
32 (and 1 if the carry flag was set by
33 the previous addition) and store it
34 Add zero(+ 1 if the carry flag is set)
35 to the contents of 03E2
36 03E0, 03E1, 03E2 contain the total time
37 Check 57471 to see if the next PA
38 line is a 1 or a 0 using 0336
39 0336 contains a 2: binary 00000010
40 If a 0, loop: if a 1, then arithmetic
41 shift left the value in 0336: see text
42 Store the three values representing
43 the elapsed time using the current
44 value of y (It is 0 to start with)
45
46
47
48 Clear the carry flag before addition
49 Transfer y, a counter, to the accum
50 Add 177 to it to make it the ASCII
51 code for y and store it on screen
52 Increment y (next time measurement)
53 Compare y with 7: If y is less than 7
54 then go back to start of timing cycle
55 If y is 7, the program is over: clear
56 interrupt flag and return to BASIC

Program 1.

TIMER	          $033A SYS 826
1 033A 78            SEI
2 033B A9 BF         LDA #BF
3 033D 8D 00 80      STA 8000
4 0340 A2 02         LDX #02
5 0342 8E 36 03      STX 0336
6 0345 A0 19         LDY #19
7 0347 A9 00         LDA #00
8 0349 99 DF 03      STA 03DF, Y
9 034C 88            DEY
10 034D C0 00        CPY #00
11 034F D0 F6        BNE 0347
12 0351 AD 4F E8     LDA E84F
13 0354 29 01        AND #01
14 0356 F0 F9        BEQ 0351
15 0358 A9 92        LDA #92
16 035A 8D 00 80     STA 8000
17 035D A0 FF        LDY #FF
18 035F A2 FF        LDX #FF
19 0361 CA           DEX
20 0362 D0 FD        BNE 0361
21 0364 88           DEY
22 0365 D0 FA        BNE 0361
23 0367 AD 4F E8     LDA E84F
24 036A 29 01        AND #01
25 036C D0 F9        BNE 0367
26 036E A9 94        LDA #94
27 0370 8D 00 80     STR 8000
28 0373 18           CLC
29 0374 AD E0 03     LDA 03E0
30 0377 69 01        ADC #01
31 0379 8D E0 03     STA 03E0
32 037C AD E1 03     LDA 03E1
33 037F 69 00        ADC #00
34 0381 8D E1 03     STA 03E1
35 0384 AD E2 03     LDA 03E2
36 0387 69 00        ADC #00.
37 0389 8D E2 03     STA 03E2
38 038C AD 4F E8     LDA E84F
39 038F 2D 36 03     AND 0336
40 0392 F0 DF        BEQ 0373
41 0394 0E 36 03     ASL 0336
42 0397 AD E0 03     LDA 03E0
43 039A 99 E3 03     STA 03E3, Y
44 03A0 AD E1 03     LDA 03E1
45 03A0 99 EA 03     STA 03EA, Y
46 03A3 AD E2 03     LDA 03E2
47 03A6 99 F1 03     STA 03F1, Y
48 03A9 18           CLC
49 03AA 98           TYA
50 03AB 69 B1        ADC #B1
51 03AD 8D 00 80     STA 8000
52 03B0 C8           INY
53 03B1 C0 07        CPY #07
54 03B3 D0 BE        BNE 0373
55 03B5 58           CLI
56 03B6 60           RTS

Program 2.

READY.

10 REM TIMER BASIC
20 REM ROBERT MACNAUGHTON OCT 5/80
25 REM 2124 GREENHURST AVE
30 REM MISSISSAUGA   L4X 1J6
35 REM THE MACHINE LANGUAGE PROGRAM MEASURES 7 TIMES DURING A SINGLE TRIP
40 REM UP TO 8 PHOTOTRANSISTORS ARE CONNECTED TO PA0-7
45 REM SYS 826 ACTIVATES THE TIMER AND? APPEARS
50 REM WHEN PA0 IS BLOCKED OFF, RAPPEARS AND THE TIMER IS READY TO START
60 REM WHEN LIGHT AGAIN FALLS ON PA0, THE TIMER STARTS AND T APPEARS
70 REM AS EACH OF PA1-7 IS CUT OFF, THE TOTAL ELAPSED TIME IS STORED
75 REM AS EACH MEASUREMENT IS MADE, ITS NUMBER APPEARS (1–7)
80 REM UNUSED PA LINES SHOULD BE OPEN CIRCUITS
200 PRINT "ĥ"205 INPUT "↓NUMBER OF RUNS" ; NR
210 FORJ = 1TONR
215 SYS826
220 FOR I = 0TO6
225 REM THE NEXT STATEMENT CALCULATES THE TIMES
226 REM THE MEMORY LOCATIONS FOR THE TIMES ARE (995, 1002, 1009)(996, 1003, 1010),
227 REM CONTINUING UP TO (1001, 1008, 1015)
228 REM EACH TIMING CYCLE TAKES 43 MACHINE LANGUAGE STEPS OR 43 MICROSECONDS
230 T(I, J) = 43*(PEEK (995 + I) + PEEK(1002 + I) *256 + PEEK (1009 + I) *256*256)/1000000
240 REM NEXT STATEMENT ROUNDS OFF THE TIMES TO 1/10000 S
250 T(I, J) = INT (T(I, J)*10000)/10000
260 PINT T (I, J),
270 NEXT: PRINT: PRINT: NEXT
280 REM CALCULATE THE AVERAGE TIMES
290 PRINT "AVERAGE TIMES"
300 FOR I = 0TO6: TM (I) = 0: FOR J = 1 TONR
310 TM(I) = TM (I) + T (I, J)
320 AV(I) = TM (I)/NR
330 AV(I) = INT(AV(I)*10000)/10000
340 NEXT: NEXT
350 FOR I = 0 TO6: PRINTAV(I), : NEXT: PRINT
400 GOTO 205