While reading the documentation about reading floppy disks with basic, I’ve found another interesting sample program which I want to share here.

The book I have available for the Alphatronic is “Das große Alphatronic-BASIC-BUCH” by Bodo and Norbert Bollow. They have written a chapter about creating random numbers by using the floppy drive: When a disk is in the drive and is spinning, we can get a signal whenever the index hole is coming round. The disk is however at an arbitrary position, so it takes an arbitrary time until the index hole is there. This timing can be used to create a random number.

If the time between two index holes is measured, you can calculate, how fast the disk spins. If you do this measurement over and over again, you can do some statistics on the numbers to see, how accurate the floppy drive motor is. That is exactly what the example program is about.

In order to access the information, whether the index hole is currently detected, one needs to ask the floppy controller. While this can be done directly in BASIC with INP for reading from a I/O port, this access from BASIC is very slow and the rotation speed will not be very accurate. Therefore a little machine code program is used with a simple counter, to measure the timings.

Let’s first have a look at the machine program in assembler:

                               ORG     0E4E0H
                               PUBLIC  ZUFALL
E4E0'  3E D4           ZUFALL: MVI     A,0D4H   ;FORCE-INTERRUPT-Maske laden
E4E2'  D3 50                   OUT     50H      ;und ausgeben
E4E4'  06 40                   MVI     B,40H    ;Maske (Interrupt-Erkennung)
E4E6'  11 0000                 LXI     D,0      ;lösche Zählregisterpaar
E4E9'  DB 50                   IN      50H      ;Vorsichtsmaßnahme (bei IN wird
E4EB'  DB 54                   IN      54H      ;                   IRQ gelöscht)
E4ED'  13              LOOP:   INX     D        ;zählen
E4EE'  DB 54                   IN      54H      ;Statusregister laden
E4F0'  A0                      ANA     B        ;mit Interrupt-Maske vergleichen
E4F1'  CA E4ED'                JZ      LOOP     ;zurück, falls kein Interrupt
E4F4'  73                      MOV     M,E      ;Zählerstand (low Byte) nach Memory
E4F5'  23                      INX     H        ;
E4F6'  72                      MOV     M,D      ;Zählerstand (high Byte) nach Memory
E4F7'  C9                      RET

Here’s the BASIC program. It uses the above machine program but putting it at a different origin into memory, so the jump address E4ED is adjusted.

10 '***************************************************************
20 '*                                                             *
30 '*   Programm LAUFWERK         ermittelt Ganggenauigkeit des   *
40 '*                             Diskettenmotors                 *
50 '*                                                             *
60 '*   Ein Maschinensprache-Unterprogramm prüft, ob das Index-   *
70 '*   loch der Diskette im zuletzt benutzten Laufwerk           *
80 '*   vorüberkommt                                              *
90 '*                                                             *
100 '***************************************************************
110 CLEAR , &HFFE7
120 DEFINT D
130 GOSUB 1000                 'Maschinenunterprogramm bereitstellen
140 PRINT"Im zuletzt benutzten Laufwerk muß eine Diskette liegen!"
150 INPUT"Wie oft soll die Umdrehungszeit gemessen werden"; DANZ
160 DIM DAT(DANZ+1),KURV(160)
170 FOR D=0 TO DANZ+1
180     CALL ZUFALL(ZAEHL%)
190     DAT(D)=ZAEHL%
200 NEXT
210 PRINT"Daten fertig"
220 REM erster Durchlauf: MIN, MAX, MIWERT ermitteln
230 MIN=DAT(2) :MAX=MIN :SUM=0
240 FOR I=2 TO DANZ+1
250     IF DAT(I)<MIN THEN MIN=DAT(I)
260     IF DAT(I)>MAX THEN MAX=DAT(I)
270     SUM=SUM+DAT(I)
280 NEXT
290 PRINT"Minimum, Maximum und Summe fertig:";
300 PRINT MIN,MAX,SUM
310 REM Ermitten der Kurve KURV
320 DIF=MAX-MIN
330 FAK=DIF/160            'Streckungsfaktor
340 IF FAK>INT(FAK) THEN FAK=INT(FAK)+1
350 FOR I=1 TO 160
360     KURV(I)=0
370 NEXT
380 FOR I=2 TO DANZ+1
390     Y=INT( (DAT(I)-MIN) / FAK )+1
400     KURV(Y)=KURV(Y)+1
410 NEXT
420 PRINT"Häufigkeitsverteilung fertig"
430 REM Ermitteln der Maximums von KURV
440 MAXK=0
450 FOR I=1 TO 160
460     IF MAXK<KURV(I) THEN MAXK=KURV(I)
470 NEXT
480 IF MAXK<72 THEN 540
490 REM Korrekturfaktor zum Begrenzen der Höhe auf 72 "Zeilen"
500 FAK2=72/MAXK
510 FOR I=1 TO 160
520     KURV(I)=INT(KURV(I)*FAK2)
530 NEXT
540 REM Ausgeben der Kurve
550 DIS 12
560 MIW=SUM/DANZ
570 PRINT$(1,45),"min:";MIN/100000!;"s"
580 PRINT$(2,45),"max:";MAX/100000!;"s"
590 PRINT$(3,45),"Abstand MAX-MIN ";(MAX-MIN)/MIW*100;" %"
600 PRINT$(4,45), "Mittelwert:";MIW/100000!;" also"
610 PRINT$(5,45),"ca.";INT(6E+06/MIW+.5);"Umdrehungen pro Minute"
620 PRINT$(6,45),"Ungenauigkeit:";DIF*10;"Mikrosekunden"
630 FOR I=1 TO 160
640     IF KURV(I)>0 THEN LINE(I,72)-(I,73-KURV(I)),PSET
650 NEXT
660 PRINT$(1,1),;
670 END
1000 ' Lesen des Unterprogramms
1010 I=&HFFE8
1020 ZUFALL=&HFFE8
1030    READ D$
1040    D=VAL("&H"+D$)
1050    IF D>255 THEN RETURN
1060    POKE I,D
1070    I=I+1
1080 GOTO 1030
1090 DATA 3E,D4,D3,50,06,40,11,00,00,DB,50,DB,54,13,DB,54
1100 DATA A0,CA,F5,FF,73,23,72,C9,E0F

So, how does this work? The machine program has a loop in which the counter (in register D) is incremented until the floppy controller says “index detected”. The floppy controller is queried in every iteration (IN 0x54). The instructions each take a specific number of cycles and this determines, how much time is passed. Let’s see, we have these instructions inside the loop: INX, IN, ANA, JZ. INX = 5 cycles, ANA = 4 cycles, IN = 10 cycles, JZ = 10 cycles. In sum, this is 29 cycles, which is roughly 30 cycles. Since the Alphatronic 8085A CPU runs a 3 MHz, one loop duration is 1/100000 seconds. That is exactly the factor used in lines 570, 580 and 600.

The more interesting value is the number of rotations per minute. That’s calculated in line 610. 6E+06 is actually “60 * 100000”. The “+.5” is a trick for rounding up, since the result is converted into a integer.

Here’s a screenshot from the results:

I’m a bit surprised to read, that the floppy drive rotates the disk at 390 rpm! That’s way too fast. The drive is working and I know, it is a double density 5.25” drive. The rotation speed must be around 300 rpm. So, something with the measurement seems to be off.

One small difference is the loop duration: 29 vs. 30 cycles. But calculating it with 29 cycles makes the number even more wrong - it goes into 400 rpm.

Obviously, we have the loop duration too short. It seems to be not only 30 cycles. It must be more. I have a theory, which I need to test: In lines 180 we call the machine program and in line 190 we store the counter into the array DAT and then we loop again. Switching from the BASIC interpreting into machine mode and back and storing the data, incrementing the (outer) loop variable D and actually looping all should be part of the measurement. And this is not part of the 30 cycles…

We’d need a machine program, that actually counts the time between to index hole detections. The program we use here only measures the time to the next index hole. And that’s the issue. By the way, the first result of the machine program is discarded, as this would be a random number. Depending on the current position of the floppy, the index hole might arrive immediately or it needs a whole rotation. That’s why the loop in line 240 starts at index 2. Actually the first two measurements are discarded, not sure why. I would assume, discarding only one should be sufficient. You can see the origin of the machine program by looking at the name. It’s still called “ZUFALL” (“randomizer”).

To be continued…