!***********************************************************************
!*
!*          Magnetic tape support routines for utility programs
!*
!*                  R.R. McLeod   ERCC   MCMLXXVIII
!*                  R.D. Eager    UKC    MCMLXXXII
!*
!***********************************************************************
!
!
!***********************************************************************
!*
!* 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)
end ;   ! of DENSITYMAG
!
!
externalroutine  modemag(integer  chan,tmode)
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)
s = s." " while  length(s) < 6 
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 = 0
dflag = dmag io(flag,control,len,6,sno(chan),0);   ! Rewind to BT
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 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