!***********************************************************************
!*
!*          Magnetic tape support routines for utility programs
!*
!*                  R.R. McLeod   ERCC   MCMLXXVIII
!*                  R.D. Eager    UKC    MCMLXXX
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!* 16/11/79 - Accept tape claims with '?' as the last character of the
!*            volume name: this means 'load with optional ring'.
!*          - Different handling of failures: if flag is 2, then a
!*            catastrophic failure has occurred (deck powered off, etc):
!*            if flag is 1, then a hardware fault has occurred - allow
!*            MAXFAULTCOUNT of these on a channel, then abandon.
!* 03/01/80 - Additional routines DENSITYMAG, MODEMAG (DENSITYMT,
!*            MODEMT) to enable use of 800 bpi tapes, and 1900
!*            series compress/expand mode tapes.
!*          - Channel number now given in diagnostics.
!*          - Corrected code for '*' and '?' checks on volume name.
!*          - Additional routine ASKMAG (ASKMT) for requesting a tape,
!*            whilst retaining control if it not available.
!* 08/04/80 - Correction to code of ASKMAG, to return zero flag at BT.
!***********************************************************************
!
!
!***********************************************************************
!*
!*          Constants
!*
!***********************************************************************
!
CONSTINTEGER  MAXCHAN = 8
CONSTINTEGER  MAXFAULTCOUNT = 10;   ! Abandon after 10 hardware faults
!
!
!***********************************************************************
!*
!*          Own variables
!*
!***********************************************************************
!
OWNINTEGERARRAY  MODE(1:MAXCHAN);                ! Read/write mode words
OWNINTEGERARRAY  CONT(1:MAXCHAN) = 0(MAXCHAN);   ! Control words
OWNINTEGERARRAY  SNO(1:MAXCHAN) = -1(MAXCHAN);   ! Service numbers
OWNINTEGERARRAY   FAULTCOUNT(1:MAXCHAN);         ! Count of hardware faults
OWNSTRING (6)ARRAY  VOL(1:MAXCHAN);              ! Volume identifiers
!
!
!***********************************************************************
!*
!*          External references
!*
!***********************************************************************
!
EXTERNALINTEGERFNSPEC  DMAG CLAIM(STRING (6) TSN,INTEGERNAME  SNO,C 
                                  INTEGER  REQ,MODE)
EXTERNALINTEGERFNSPEC  DMAG IO(INTEGERNAME  REPLY FLAG,CONTROL,LEN,C 
                               INTEGER  TYPE,SNO,ADR)
SYSTEMSTRINGFNSPEC  ITOS(INTEGER  N)
ROUTINESPEC  SKIPTMMAG(INTEGER  CHAN,N)
!
!
!***********************************************************************
!*
!*          Internal routines
!*
!***********************************************************************
!
ROUTINE  FAIL(STRING (255) S,INTEGER  CHAN)
SELECTOUTPUT(0)
NEWLINES(2)
PRINTSTRING("*** Error - ".S." - channel ".ITOS(CHAN)." ***")
NEWLINE
MONITOR 
STOP 
END ;   ! of FAIL
!
!
ROUTINE  CHECK CHANNEL(INTEGER  CHAN)
UNLESS  1 <= CHAN <= MAXCHAN THEN  FAIL("Invalid channel",CHAN)
END ;   ! of CHECK CHANNEL
!
!
ROUTINE  CHECK CLAIMED(INTEGER  CHAN)
IF  SNO(CHAN) < 0 THEN  FAIL("Tape not claimed",CHAN)
END ;   ! of CHECK CLAIMED
!
!
ROUTINE  RECORDFAULT(INTEGER  CHAN,INTEGERNAME  FLAG)
IF  FLAG = 2 THEN  FAIL("Catastrophic tape failure",CHAN)
IF  FAULTCOUNT(CHAN) >= MAXFAULTCOUNT THEN  START 
   FAIL("More than ".ITOS(MAXFAULTCOUNT)." tape failures",CHAN)
FINISH 
FAULTCOUNT(CHAN) = FAULTCOUNT(CHAN) + 1
FLAG = 2;   ! For return to user
END ;   ! of RECORDFAULT
!
!
!***********************************************************************
!*
!*          T H E   S U P P O R T   R O U T I N E S
!*
!***********************************************************************
!
EXTERNALROUTINE  DENSITYMAG(INTEGER  CHAN,DENSITY)
CHECK CHANNEL(CHAN)
IF  DENSITY = 800 THEN  START 
   CONT(CHAN) = CONT(CHAN)!X'80';   ! Insert '800 bpi' control bit
FINISH  ELSE  START 
   IF  DENSITY = 1600 THEN  START 
      CONT(CHAN) = CONT(CHAN) & X'7F';   ! Remove '800 bpi' control bit
   FINISH  ELSE  FAIL("Invalid density",CHAN)
FINISH 
END ;   ! of DENSITYMAG
!
!
EXTERNALROUTINE  MODEMAG(INTEGER  CHAN,TMODE)
CHECK CHANNEL(CHAN)
IF  TMODE = 1900 THEN  START 
   CONT(CHAN) = CONT(CHAN)!X'40';   ! Insert compress/expand control bit
FINISH  ELSE  START 
   IF  TMODE = 2900 THEN  START 
      CONT(CHAN) = CONT(CHAN) & X'BF';   ! Remove compress/expand control bit
   FINISH  ELSE  FAIL("Invalid mode", CHAN)
FINISH 
END ;   ! of MODEMAG
!
!
EXTERNALROUTINE  ASKMAG(INTEGER  CHAN,STRING (7) S,INTEGERNAME  FLAG)
INTEGER  DFLAG,CONTROL,LEN
!
CHECK CHANNEL(CHAN)
IF  SNO(CHAN) >= 0 THEN  FAIL("Channel already in use",CHAN)
S = " " IF  LENGTH(S) = 0
IF  CHARNO(S,LENGTH(S)) = '?' THEN  START ;   ! Select optional ring
   MODE(CHAN) = 3
   LENGTH(S) = LENGTH(S) - 1
FINISH  ELSE  START 
   IF  CHARNO(S,LENGTH(S)) = '*' THEN  START ;   ! Select read/write or read only
      MODE(CHAN) = 2
      LENGTH(S) = LENGTH(S) - 1
   FINISH  ELSE  START 
      MODE(CHAN) = 1
   FINISH 
FINISH 
UNLESS  1 <= LENGTH(S) <= 6 THEN  FAIL("Invalid volume label",CHAN)
WHILE  LENGTH(S) < 6 THEN  S = S." "
VOL(CHAN) = S
FLAG = DMAG CLAIM(S,SNO(CHAN),0,MODE(CHAN))
RETURN  IF  FLAG # 0
IF  MODE(CHAN) = 3 THEN  MODE(CHAN) = 2;   ! If ring optional, let user beware
FAULTCOUNT(CHAN) = 0;   ! Reset count of faults
CONTROL = CONT(CHAN);   ! Set mode and/or density
DFLAG = DMAG IO(FLAG,CONTROL,LEN,6,SNO(CHAN),0);   ! Rewind to BT to set mode and/or density
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in ASKMAG",CHAN)
IF  FLAG = 4 THEN  FLAG = 0;   ! Advisory flag only
END ;   ! of ASKMAG
!
!
EXTERNALROUTINE  OPENMAG(INTEGER  CHAN,STRING (7) S)
INTEGER  FLAG
!
ASKMAG(CHAN,S,FLAG)
IF  FLAG # 0 THEN  FAIL("Failure to claim tape",CHAN)
END ;   ! of OPENMAG
!
!
EXTERNALROUTINE  UNLOADMAG(INTEGER  CHAN)
INTEGER  FLAG
!
CHECK CHANNEL(CHAN)
RETURN  IF  SNO(CHAN) < 0
FLAG = DMAG CLAIM(VOL(CHAN),SNO(CHAN),1,MODE(CHAN));   ! Give back tape
VOL(CHAN) = ""
SNO(CHAN) = -1
CONT(CHAN) = 0;   ! Reset density and mode
END ;   ! of UNLOADMAG
!
!
EXTERNALROUTINE  READMAG(INTEGER  CHAN,AD,INTEGERNAME  LEN,FLAG)
INTEGER  DFLAG,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
IF  LEN <= 0 THEN  FAIL("Invalid length for read",CHAN)
CONTROL = 4;   ! Ignore short block indication
DFLAG = DMAG IO(FLAG,CONTROL,LEN,1,SNO(CHAN),AD)
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in READMAG",CHAN)
IF  FLAG # 0 THEN  START 
   IF  FLAG = 4 THEN  START ;   ! Hit tape mark
      SKIPTMMAG(CHAN,1);   ! Skip over tape mark
      FLAG = 1
   FINISH  ELSE  RECORDFAULT(CHAN,FLAG);   ! Read failure
FINISH 
END ;   ! of READMAG
!
!
EXTERNALROUTINE  WRITEMAG(INTEGER  CHAN,AD,LEN,INTEGERNAME  FLAG)
INTEGER  DFLAG,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
IF  MODE(CHAN) = 1 THEN  FAIL("Writing not allowed",CHAN)
CONTROL = 0
DFLAG = DMAG IO(FLAG,CONTROL,LEN,2,SNO(CHAN),AD)
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in WRITEMAG",CHAN)
IF  FLAG # 0 THEN  RECORDFAULT(CHAN,FLAG)
END ;   ! of WRITEMAG
!
!
EXTERNALROUTINE  WRITETMMAG(INTEGER  CHAN,INTEGERNAME  FLAG)
INTEGER  DFLAG,LEN,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
IF  MODE(CHAN) = 1 THEN  FAIL("Writing not allowed",CHAN)
CONTROL = 0
DFLAG = DMAG IO(FLAG,CONTROL,LEN,10,SNO(CHAN),0)
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in WRITETMMAG",CHAN)
IF  FLAG # 0 THEN  RECORDFAULT(CHAN,FLAG)
END ;   ! of WRITETMMAG
!
!
EXTERNALROUTINE  REWINDMAG(INTEGER  CHAN)
INTEGER  DFLAG,FLAG,LEN,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
CONTROL = 0
DFLAG = DMAG IO(FLAG,CONTROL,LEN,6,SNO(CHAN),CONTROL)
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in REWINDMAG",CHAN)
END ;   ! of REWINDMAG
!
!
EXTERNALROUTINE  SKIPMAG(INTEGER  CHAN,N)
! Skips N blocks (a tape mark counting as a block) backwards or forwards
INTEGER  FLAG,DIRECTION,I,DFLAG,LEN,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
RETURN  IF  N = 0;   ! Null call
IF  N > 0 THEN  DIRECTION = 1 ELSE  N = -N AND  DIRECTION = -1
CYCLE  I = 1,1,N
   CONTROL = 0
   LEN = DIRECTION
   DFLAG = DMAG IO(FLAG,CONTROL,LEN,8,SNO(CHAN),0);   ! Try to skip one block
   IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in SKIPMAG",CHAN)
   IF  1 <= FLAG <= 2 THEN  RECORDFAULT(CHAN,FLAG)
   IF  FLAG = 4 THEN  START ;   ! Found tape mark
      CONTROL = 1;   ! Treat tape mark as block
      LEN = DIRECTION
      DFLAG = DMAG IO(FLAG,CONTROL,LEN,9,SNO(CHAN),0);   ! Try to skip the tape mark
      IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in SKIPMAG",CHAN)
      IF  1 <= FLAG <= 2 THEN  RECORDFAULT(CHAN,FLAG)
   FINISH 
REPEAT 
END ;   ! of SKIPMAG
!
!
EXTERNALROUTINE  SKIPTMMAG(INTEGER  CHAN,N)
INTEGER  FLAG,DFLAG,LEN,DIRECTION,I,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
IF  N = 0 THEN  RETURN ;   ! Null call
IF  N > 0 THEN  DIRECTION = 1 ELSE  N = -N AND  DIRECTION = -1
CYCLE  I = 1,1,N
   CONTROL = 1;   ! Treat tape mark as block
   LEN = DIRECTION
   DFLAG = DMAG IO(FLAG,CONTROL,LEN,9,SNO(CHAN),0)
   IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in SKIPTMMAG",CHAN)
   IF  1 <= FLAG <= 2 THEN  RECORDFAULT(CHAN,FLAG)
REPEAT 
END ;   ! of SKIPTMMAG
!
!
EXTERNALROUTINE  FSKIPTMMAG(INTEGER  CHAN,N,INTEGERNAME  FLAG)
INTEGER  DFLAG,LEN,CONTROL
!
CHECK CHANNEL(CHAN)
CHECK CLAIMED(CHAN)
IF  N = 0 THEN  RETURN ;   ! Null call
CONTROL = 1;   ! Treat tape mark as block
LEN = N;   ! Number of tapemarks to skip
DFLAG = DMAG IO(FLAG,CONTROL,LEN,9,SNO(CHAN),0)
IF  DFLAG > 7 THEN  FAIL("DMAG IO fails in SKIPTMMAG",CHAN)
IF  1 <= FLAG <= 2 THEN  RECORDFAULT(CHAN,FLAG)
IF  FLAG = 4 THEN  FLAG = 1;   ! Found double tape mark before skipping enough
END ;   ! of FSKIPTMMAG
!
!
!***********************************************************************
!*
!*          Routines for back-compatibility
!*
!***********************************************************************
!
EXTERNALROUTINE  ASKMT(STRING (7) VOL,INTEGERNAME  FLAG)
ASKMAG(1,VOL,FLAG)
END ;   ! of ASKMAG
!
!
EXTERNALROUTINE  OPENMT(STRING (7) VOL)
OPENMAG(1,VOL)
END ;   ! of OPENMT
!
!
EXTERNALROUTINE  UNLOADMT
UNLOADMAG(1)
END ;   ! of UNLOADMT
!
!
EXTERNALROUTINE  REWINDMT
REWINDMAG(1)
END ;   ! of REWINDMT
!
!
EXTERNALROUTINE  READMT(INTEGER  AD,INTEGERNAME  LEN,FLAG)
READMAG(1,AD,LEN,FLAG)
END ;   ! of READMT
!
!
EXTERNALROUTINE  WRITEMT(INTEGER  AD,LEN,INTEGERNAME  FLAG)
WRITEMAG(1,AD,LEN,FLAG)
END ;   ! of WRITEMT
!
!
EXTERNALROUTINE  WRITETMMT(INTEGERNAME  FLAG)
WRITETMMAG(1,FLAG)
END ;   ! of WRITETMMT
!
!
EXTERNALROUTINE  SKIPMT(INTEGER  N)
SKIPMAG(1,N)
END ;   ! of SKIPTM
!
!
EXTERNALROUTINE  SKIPTMMT(INTEGER  N)
SKIPTMMAG(1,N)
END ;   ! of SKIPTMMT
!
!
EXTERNALROUTINE  FSKIPTMMT(INTEGER  N,INTEGERNAME  FLAG)
FSKIPTMMAG(1,N,FLAG)
END ;   ! of FSKIPTMMT
!
!
EXTERNALROUTINE  DENSITYMT(INTEGER  DENSITY)
DENSITYMAG(1,DENSITY)
END ;   ! of SETMT
!
!
EXTERNALROUTINE  MODEMT(INTEGER  TMODE)
MODEMAG(1,TMODE)
END ;   ! of MODEMT
ENDOFFILE