The following is a small HP-71B BASIC program that I have written for Mark Power three years ago.
Mark needed to dump his mini-cassette content in a raw format, maybe this will help recover your failed tape content.
Code:
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