La particularité de cette cassette est qu'elle était compatible au lecteur HP-82161A mais que son formatage lui ne l'était pas.
PVI, le format utilisé pour les médiums de stockage par les contrôleurs HP-IL de poche (41, 71 & 75) s'appelle Logical Interchange Format ou LIF.
La conséquence de ce format non-LIF est qu'aucune machine HP n'est capable de lire la cassette avec les fonctions habituelles.
Donc, pour récupérer les données, j'ai été obligé d'écrire un programme qui utilise que les commandes HP-IL de bas niveau.
Afin de limiter la complexité d'utiliser deux périphériques, un pour la lecture et l'autre pour l'écriture, j'ai décidé de sauvegarder le contenu de la cassette (128K) dans l'ordinateur de poche. (HP-71B)
Le fichier final à été transféré avec la PIL-Box sur un ordinateur pour traitement ultérieur.
Matériels nécessaires pour la lecture de la cassette:
- HP-71B (~16K de RAM interne)
- 82401A module HP-IL pour HP-71
- 128K de RAM additionnel (modules CMT, HHP, FRAM71, etc)
- 82167x deux câbles HP-IL
- 82161A lecteur de cassette numérique
- 82176A cassette numérique
Programme d'extraction ...
Code : Tout sélectionner
10 ! TAPEDUMP @Copyrights 2021, Sylvain Cote
11 ! last updated on: 2021-03-20 21h10 EDT
12 ! Program: do a sector by sector tape dump to a file in main memory (~130KB)
13 ! Needs : 71B, 82401A, ~140KB of main RAM
30 ! A : DriveAddress (constant)
31 ! B : DriveBusyFlag
32 ! C : DriveErrorCode
33 ! D : DeviceAccessoryID
34 ! E : DriveErrorFlag
35 ! L : DriveErrorLevel
36 ! L0 : InputBufferLength
37 ! L2 : LogDriveError
38 ! R0 : ActiveReadTimes
39 ! R1 : MaxReadTimes (constant)
40 ! S : DriveStatus
41 ! S0 : TapeSectorActive
42 ! S1 : TapeSectorNumbers (constant)
43 ! S2 : TapeSectorSize (constant)
44 ! S3 : TapeSeekFlag : tell the drive to do a tape seek if set to 1
45 ! T1 : TapeTrackNumbers (constant)
50 ! B0$ : InputBuffer
51 ! M$ : DriveErrorLevel
100 OPTION BASE 0 ! array index start at 0
105 DIM B0$[256] ! input buffer creation
110 IMAGE #,256A ! input format
115 A=1 @ R1=3 @ T1=2 @ S1=256 @ S2=256 ! app constants
120 S=0 @ B=0 @ E=0 @ C=0 @ L=0 @ M$="" ! init values
125 B0$="" @ L0=0 @ D=0 @ L2=0 @ R0=0 ! init values
130 SFLAG -1 @ PURGE TD @ PURGE TE @ CFLAG -1 ! delete old files, disregard error if files are missing
135 CREATE TEXT TD @ CREATE TEXT TE ! create TapeDump and TapeError files
140 ASSIGN #1 TO TD @ ASSIGN #2 TO TE ! open TapeDump and TapeError files
145 SFLAG -23 ! use variable size as tape read length
150 DELAY 0,0 ! set display and scrolling delays
155 RESET HPIL @ RESTORE IO ! reset loop
160 GOSUB 'AIDCHECK' ! verify that we have a tape drive at specified loop address
165 GOSUB 'REWIND' ! rewind tape
170 S3=1 ! seek tape to track 0 sector 0
175 FOR T0=0 TO T1-1 ! track outer loop
180 FOR S0=0 TO S1-1 ! sector inner loop
185 GOSUB 'READSEC' ! read sector from tape
190 NEXT S0 ! sector inner loop end
195 NEXT T0 ! track outer loop end
200 GOSUB 'REWIND' ! rewind tape
205 ASSIGN #1 TO * @ ASSIGN #2 TO * ! close files
210 CFLAG -23 @ DELAY 1,1/8 ! restore environment
215 DISP "DUMP DONE" ! warn
220 END ! end of program
500 ! device accessory id check !
505 'AIDCHECK': ! IN: A / UPD: D / OUT: - ! sub check device type at loop address
510 ! D=DEVAID(A) @ IF D DIV 16#1 THEN DISP "Err: Invalid Device" @ STOP ! get accessory id and if device type is a mass storage then stop the program
515 D=DEVAID(A) @ IF D#16 THEN DISP "Err: Invalid Device" @ STOP ! get accessory id and if device is not a 82161A then stop the program
520 RETURN ! end sub
525 ! loop until drive is ready !
530 'BUSYWAIT': ! IN: A / UPD: S / OUT: - ! sub wait until drive is ready
535 'BW': S=SPOLL(A) @ IF BIT(S,5)=1 THEN GOTO 'BW' ! loop until drive is no longer busy
540 RETURN ! end sub
545 ! reset drive status variables !
550 'RSETSTAT': ! IN: A / UPD: S,B,E,C,L / OUT: - ! sub reset status variables
555 S=0 @ B=0 @ E=0 @ C=0 @ L=0 ! set drive status related variables to 0
560 RETURN ! end sub
565 ! read & decode drive status byte !
570 'READSTAT': ! IN: A / UPD: - / OUT: S,B,E,C,L ! sub read and decode drive status
575 S=SPOLL(A) @ B=BIT(S,5) @ E=BIT(S,4) @ C=BINAND(S,15) ! read drive status and get busy flag, error flag and error code
580 IF E=0 AND B=0 THEN L=0 @ RETURN ! if drive not in error and not busy -> set error level to normal and return
585 IF E=0 AND B=1 THEN L=1 @ RETURN ! if drive not in error but is busy -> set error level to warning and return
590 IF C=9 OR C=10 THEN L=1 ELSE L=2 ! if drive error is recoverable -> set error level to warning otherwise set error level to
595 RETURN ! end sub
600 ! build status message !
605 'BUILDMSG': ! IN: B,E,C,L / UPD: - / OUT: M$ ! sub build drive status message
610 IF E=0 AND B=0 THEN M$="" @ RETURN ! drive is normal, return
615 IF E=0 AND B=1 THEN M$="Drive Busy" @ GOTO 'BM' ! drive is busy
620 IF C=9 THEN M$="Rec Number" @ GOTO 'BM' ! unexpected record number, tape read failed, try to re-read the sector
625 IF C=10 THEN M$="Rec Checksum" @ GOTO 'BM' ! invalid record checksum, tape read failed, try to re-read the sector
630 IF C=7 THEN M$="New Tape" @ GOTO 'BM' ! new tape has been inserted in the drive and need to be seeked
635 IF C=4 THEN M$="No Tape" @ GOTO 'BM' ! there is no tape in drive
640 IF C=8 THEN M$="Time Out" @ GOTO 'BM' ! no data record has been found on the tape
645 IF C=3 THEN M$="Tape:EOT+TS" @ GOTO 'BM' ! unexpected end of tape has been reached AND tape has stalled
650 IF C=1 THEN M$="End of Tape" @ GOTO 'BM' ! unexpected end of tape has been reached
655 IF C=2 THEN M$="Tape Stalled" @ GOTO 'BM' ! tape has stalled
660 IF C=5 OR C=6 THEN M$="Device" @ GOTO 'BM' ! generic device error
665 IF C=12 THEN M$="Tape Size" @ GOTO 'BM' ! track number greather than 1 has been requested
670 M$="Unknown" ! unknown error (covers code=0, 11, 13, 14 & 15)
675 'BM': ! build message ! build message
680 IF L=1 THEN M$="Wrn:"&STR$(C)&":"&M$ ! warning message
685 IF L=2 THEN M$="Err:"&STR$(C)&":"&M$ ! error message
690 RETURN ! end sub
695 ! verify that drive is ready and not in error !
700 'PRECHECK': ! IN: A / UPD: S,B,E,C,L,M$ / OUT: - ! sub pre-check before doing a drive operation
705 GOSUB 'BUSYWAIT' @ GOSUB 'READSTAT' @ M$="" ! wait for drive to be ready, then read current status and decode it
710 IF L=2 THEN GOSUB 'BUILDMSG' @ DISP M$ @ STOP ! if error level is error then display error message then stop the program
715 RETURN ! end sub
720 ! rewind drive tape !
725 'REWIND': ! IN: A / UPD: S,B,E,C,L,M$ / OUT: - ! sub rewind tape
730 GOSUB 'PRECHECK' ! pre-check drive for readyness
735 DISP "Rewinding Tape ..." ! tell user the action to be taken
740 SEND UNT UNL LISTEN A DDL 7 ! rewind tape
745 GOSUB 'BUSYWAIT' ! wait for drive to be ready
750 RETURN ! end sub
755 ! position drive head to the specified track & sector !
760 'SEEK': ! IN: A,T0,S0 / UPD: S,B,E,C,L,M$ / OUT: - ! sub seek track:record
765 GOSUB 'PRECHECK' ! pre-check drive for readyness
770 DISP "Seek: T="&STR$(T0)&" S="&STR$(S0) ! tell user the action to be taken
775 SEND UNT UNL MTA LISTEN A DDL 4 DATA T0 DATA S0 ! seek tape and wait for drive to be ready
780 GOSUB 'BUSYWAIT' ! wait for drive to be ready
785 RETURN ! end sub
790 ! Set Drive in Read Mode - Drive Fill Buffer 0 with Current Sector !
795 'READMODE': ! IN: A / UPD: - / OUT: - ! sub activate data transfert mode
800 DISP "Read mode activated" ! tell user the action to be taken
805 SEND UNT UNL TALK A DDT 2 ! drive set in read mode, trigger the drive to fill buffer 0 with current sector
810 RETURN ! end sub
815 ! Read Sector !
820 'READSEC': ! IN: A,R1,T0,S0,S2,S3,#1,#2 / UPD: R0,S,B,E,C,L,M$,B0$,L0,S3 / OUT: - ! sub read sector
825 FOR R0=1 TO R1 ! read times loop
830 IF S3=1 THEN GOSUB 'SEEK' @ GOSUB 'READMODE' @ S3=0 ! activate data transfer from tape drive
835 DISP "Read T:"&STR$(T0)&" S:"&STR$(S0)&" #"&STR$(R0) ! tell user the action to be taken
840 ENTER :A USING 110;B0$ @ L0=LEN(B0$) ! get drive buffer 0, trigger the drive to fill buffer 0 with next sector
845 GOSUB 'BUSYWAIT' @ GOSUB 'READSTAT' ! wait for drive to be ready & get drive status
850 IF L=0 AND L0=S2 THEN PRINT #1;B0$ @ RETURN ! if no error and record length is valid then save record and return
855 IF L=0 THEN DISP "Wrn: Inv.Rec.Length" @ PRINT #2;B0$ @ BEEP 440,1 @ RETURN ! if no error and record length is invalid then log data and return
860 IF L=1 THEN GOSUB 'BUILDMSG' @ DISP M$ @ BEEP 523,1 @ S3=1 ! there was an problem reading the tape, warn user
865 IF L=2 THEN GOSUB 'BUILDMSG' @ DISP M$ @ STOP ! if error level is error then display error message then stop the program
870 NEXT R0 ! end loop
875 PRINT #2;":T="&DTH$(T0)[5]&":S="&DTH$(S0)[4,5]&":E="&DTH$(S)[5]&":" ! too many retry, log error (ex.: ":T=1:S=A8:E=A:")
880 DISP "Wrn: Skipped Bad Rec." @ BEEP 440,1 @ S3=1 ! warn user, next record will be seeked
885 RETURN ! end sub
- La sous-routine 'AIDCHECK' vérifie si le périphérique sélectionné est bien une unité de stockage.
- La sous-routine 'BUSYWAIT' attend que l'unité de stockage soit prête à accepter une commande HP-IL.
La fonction SPOLL du HP-71B utilise la commande HP-IL Send Status (SST) pour récupérer le statut du lecteur.Code : Tout sélectionner
HP-82161A Status Byte Definition --------------------------------- 0 00000000 Idle Condition .. 15 00001111 Idle Condition 16 00010000 Not Used 17 00010001 End Of Tape Error 18 00010010 Stall Error 19 00010011 End/Stall Error 20 00010100 No Tape Error 21 00010101 Device Error 22 00010110 Device Error 23 00010111 New Tape Error 24 00011000 Timeout Error 25 00011001 Record Number Error 26 00011010 Checksum Error 27 00011011 Not Used 28 00011100 Size Error 29 00011101 Not Used 30 00011110 Not Used 31 00011111 Not Used 32 001xxxx Busy Condition .. 63 001xxxx Busy Condition
- La sous-routine 'RSETSTAT' remet les variables reliées au statut à 0
- La sous-routine 'READSTAT' retourne un niveau d'erreur (0=normal, 1=busy ou en erreur mais gérable & 2=erreur fatal)
- La sous-routine 'BUILDMSG' crée un message d'erreur à partir du résultat du statut (SST)
- La sous-routine 'PRECHECK' vérifie que le lecteur est fonctionnel
- La sous-routine 'REWIND' rembobine la cassette
- La sous-routine 'SEEK' positionne la cassette à un endroit spécifique (piste et secteur)
- La sous-routine 'READMODE' met le lecteur en mode lecture et remplis son buffer de transmission avec le secteur sélectionné
- La sous-routine 'READSEC' lecture du secteur courant (le lecteur transmet son buffer de transmission, puis il remplis son buffer de transmission avec le secteur suivant)