//tspdos.bcpl Tape Server Do* command procedures
// g. krasner November 19, 1979
// Last modified by Tim Diebert, October 10, 1980 11:00 AM
//
get "Tapes.d"
get "PUP.decl"
get "TSP.decl"
//---------------------------------------------------------------
external
//---------------------------------------------------------------
[
//Imports
//From Tapes
OpenTape; CloseTape; TapeRewind; TapeUnload; TapeEOF
TapeStatus; ActOnTape; PerformVTCB
FileSkipFwd; FileSkipRev; RecSkipFwd; RecSkipRev; EraseInches
//From 'System'
Ws; Wo; Endofs; keys; Block; Gets; CallSwat
//From BSP
BSPWriteBlock; BSPForceOutput
//Imported Globals
rwBlock; rwKey; kbdKey; usedidnos; useddrives; ctxQ
currentVersionString
//Exports (to TSPServer)
ReplyNo; ReplyYes
DoOpenDrive; DoCloseDrive; DoReadRecord; DoWriteRecord
DoFwdSpaceRecord; DoBackSpaceRecord; DoFwdSpaceFile
DoBackSpaceFile; DoWriteEOF; DoWriteBlankTape
DoRewind; DoUnload; DoGetStatus; DoSetStatus
DoSendText; DoGetText
]
// Command Processors: Do*()
//These routines handle the processing for a received command. Most
//verify that there is a drive open (Service.drives = -1).
//Error recovery and Replying are all done at this level.
//---------------------------------------------------------------
let DoOpenDrive(ser) be
//---------------------------------------------------------------
[
let blk = ser>>Service.blk
// process the OpenDrive command, opening the given drive if allowed.
test ser>>Service.drive ge 0
ifso //this Server has a drive open already
ReplyNo(ser,openAlready)
ifnot
[
let dr = blk>>OpenDrive.driveNumber
test dr ls maxDrives
ifnot ReplyNo(ser,badDriveNo)
ifso test useddrives!dr
ifso ReplyNo(ser,driveInUse)
ifnot
[
let tape = OpenTape(dr,ser>>Service.speed,DoErrorProc)
test tape
ifnot ReplyNo(ser,badDrive)
ifso
[ //All went well!!
ser>>Service.tape = tape
ser>>Service.drive = dr
useddrives!dr = true
ReplyYes(ser,cmdOpenDrive)
]
]
]
]
//---------------------------------------------------------------
and DoCloseDrive(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then //drive is open, close it
[ let dr = ser>>Service.drive
useddrives!dr = false
CloseTape(ser>>Service.tape)
ser>>Service.tape = false
ser>>Service.drive = -1
ReplyYes(ser,cmdCloseDrive)
]
]
//---------------------------------------------------------------
and DoReadRecord(ser) be
//---------------------------------------------------------------
[ //Read a record from tape
if VerifyOpen(ser) then
[ //Do read, and return status as well
let blk = ser>>Service.blk //get command block
let count = ActOnTape(ser>>Service.tape,ReadTape,lv blk>>HereIsRecord.record,rwBlockLength,0,0,ser>>Service.retries)
if count le 0 then count = 0
if count gr rwBlockLength-24
then count = rwBlockLength-24 //truncate to fit
blk>>HereIsRecord.recordLength = count
blk>>HereIsRecord.endingStatus = (ser>>Service.tape)>>TCB.Flags
blk>>HereIsRecord.type = cmdHereIsRecord
blk>>HereIsRecord.length = 4+(count+1)/2
count = 2 * blk>>HereIsRecord.length
BSPWriteBlock(ser>>Service.bspStr,blk,0,count)
BSPForceOutput(ser>>Service.bspSoc)
]
]
//---------------------------------------------------------------
and DoWriteRecord(ser) be
//---------------------------------------------------------------
[ //Write a record to tape
if VerifyOpen(ser) then
if OkToWrite(ser) then
[ let blk = ser>>Service.blk
if blk>>WriteRecord.recordLength gr rwBlockLength-24
then blk>>WriteRecord.recordLength = rwBlockLength-24
ActOnTape(ser>>Service.tape,WriteTape,0,0,lv blk>>WriteRecord.record,blk>>WriteRecord.recordLength,ser>>Service.retries)
ReplyStatus(ser,cmdWriteRecord)
]
]
//---------------------------------------------------------------
and DoFwdSpaceRecord(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ RecSkipFwd(ser>>Service.tape) //Space Forward
ReplyStatus(ser,cmdFwdSpaceRecord) //reply with ending status
]
]
//---------------------------------------------------------------
and DoBackSpaceRecord(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ RecSkipRev(ser>>Service.tape) //Space Back
ReplyStatus(ser,cmdBackSpaceRecord) //reply with ending status
]
]
//---------------------------------------------------------------
and DoFwdSpaceFile(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ FileSkipFwd(ser>>Service.tape) //Skip File Forward
ReplyStatus(ser,cmdFwdSpaceFile) //reply with ending status
]
]
//---------------------------------------------------------------
and DoBackSpaceFile(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ FileSkipRev(ser>>Service.tape) //Skip File Backward
ReplyStatus(ser,cmdBackSpaceFile) //reply with ending status
]
]
//---------------------------------------------------------------
and DoWriteEOF(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
if OkToWrite(ser) then
[ TapeEOF(ser>>Service.tape) //Write an EOF
ReplyStatus(ser,cmdWriteEOF) //reply with ending status
]
]
//---------------------------------------------------------------
and DoWriteBlankTape(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
if OkToWrite(ser) then
[ let blk = ser>>Service.blk
let inches = blk>>WriteBlankTape.gap //get gap size
EraseInches(ser>>Service.tape,inches) //Write inches of blank tape
ReplyStatus(ser,cmdWriteBlankTape) //reply with ending status
]
]
//---------------------------------------------------------------
and DoRewind(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ PerformVTCB(ser>>Service.tape, Rewind) //initiate rewind
ReplyYes(ser,cmdRewind) //reply before rewind complete
]
]
//---------------------------------------------------------------
and DoUnload(ser) be
//---------------------------------------------------------------
[ if VerifyOpen(ser) then
[ PerformVTCB(ser>>Service.tape, Unload) //initiate Unload
ReplyYes(ser,cmdUnload) //reply before rewind complete
]
]
//---------------------------------------------------------------
and DoGetStatus(ser) be
//---------------------------------------------------------------
[ let blk = ser>>Service.blk
let tape = ser>>Service.tape
blk>>HereIsStatus.length = 6
blk>>HereIsStatus.type = cmdHereIsStatus
blk>>HereIsStatus.drive = ser>>Service.drive
blk>>HereIsStatus.retries = ser>>Service.retries
blk>>HereIsStatus.speed = ser>>Service.speed
if ser>>Service.drive ge 0 then //open tape
[ blk>>HereIsStatus.tstatus = TapeStatus(tape)
]
//Send message
BSPWriteBlock(ser>>Service.bspStr,blk,0,12)
BSPForceOutput(ser>>Service.bspSoc)
]
//---------------------------------------------------------------
and DoSetStatus(ser) be
//---------------------------------------------------------------
[ let blk = ser>>Service.blk
let which = blk>>SetStatus.selector //get selector
let set = blk>>SetStatus.newsetting
switchon which into
[
case setRetries:
[ test (set ge 0 & set le 8)
ifso [ ser>>Service.retries = set
ReplyYes(ser,cmdSetStatus) ]
ifnot ReplyNo(ser,badRetrySetting)
endcase
]
case setSpeed:
[ test ser>>Service.drive ls 0 //only set speed if not open
ifso
[ switchon set into
[
case 0: ser>>Service.speed = IPS45; ReplyYes(ser,cmdSetStatus); endcase
case 1: ser>>Service.speed = IPS125; ReplyYes(ser,cmdSetStatus); endcase
default: ReplyNo(ser,badSpeedSetting); endcase
]
]
ifnot ReplyNo(ser,openAlready)
endcase
]
default: ReplyNo(ser,badStatusSelector); endcase
]
]
//---------------------------------------------------------------
and DoSendText(ser) be
//---------------------------------------------------------------
[ //display text string to operator
let blk = ser>>Service.blk
Ws(lv blk>>SendText.text)
ReplyYes(ser,cmdSendText)
]
//---------------------------------------------------------------
and DoGetText(ser) be
//---------------------------------------------------------------
[ //Get a line of Text from operator (terminated by cr)
while kbdKey ge 0 do Block() //wait for keyboard
kbdKey = ser>>Service.idnumber //take keyboard
Ws("*nReply? ") //prompt
let blk = ser>>Service.blk
blk>>HereIsText.type = cmdHereIsText
let str = lv blk>>HereIsText.text
let echo = "x"; let i = 1; let chr = 0
until chr eq 13 do //until cr
[ while Endofs(keys) do Block() //wait for key
chr = Gets(keys)
if chr eq 8 then
[ if i gr 1 then
[ //backspace, echo \<lastchar> and backup i
i = i-1
echo>>String.char↑1 = $\;Ws(echo)
echo>>String.char↑1 = str>>String.char↑i;Ws(echo)
]
loop
]
if i ls (cmdBlockLength-2)/2 then
[ str>>String.char↑i = chr; i = i+1
echo>>String.char↑1 = chr;Ws(echo) ]
]
str>>String.length = i-1
blk>>HereIsText.length = 2+(i+1)/2 //full words plus header
BSPWriteBlock(ser>>Service.bspStr,blk,0,blk>>HereIsText.length*2)
BSPForceOutput(ser>>Service.bspSoc)
Ws("sent")
kbdKey = -1 //release beyboard
]
//---------------------------------------------------------------
and VerifyOpen(ser) = valof
//---------------------------------------------------------------
[ if ser>>Service.drive ge 0 then resultis true
ReplyNo(ser,driveNotOpened)
resultis false
]
//---------------------------------------------------------------
and OkToWrite(ser) = valof
//---------------------------------------------------------------
[ let stat = TapeStatus(ser>>Service.tape) //tape must be open!!
unless stat<<Status.FPT then resultis true
ReplyNo(ser,writeProtected)
resultis false
]
//---------------------------------------------------------------
and ReplyYes(ser,code) be
//---------------------------------------------------------------
[ //Sends back Yes reply, not switchon code for string yet
let blk = ser>>Service.blk //get command block
blk>>YesNo.type = cmdYes
blk>>YesNo.cause = doneOperation
let stat = (ser>>Service.tape)>>TCB.Flags //get tape status
blk>>YesNo.code = stat
let str = "Good Command"
YesNoMessage(blk,str,ser) //set up string and send out
]
//---------------------------------------------------------------
and ReplyNo(ser,code) be
//---------------------------------------------------------------
[ //Sends back No block, with correct string
let str = " "
switchon code into
[
case noVersion: str = "Version Check Unmade"; endcase
case noGoodMessage: str = "Illegal Command"; endcase
case openAlready: str = "Drive Already Open"; endcase
case driveInUse: str = "Drive In Use"; endcase
case badDrive: str = "Drive Bad"; endcase
case badDriveNo: str = "Bad Drive Number"; endcase
case driveNotOpened: str = "Drive Not Open"; endcase
case badStatusSelector: str = "Cannot set that Status"; endcase
case badRetrySetting: str = "Bad Setting for Retries"; endcase
case writeProtected: str = "Tape Write Protected"; endcase
case badSpeedSetting: str = "Illegal Speed Setting"; endcase
default: str = "Illegal Command"; endcase
]
let blk = ser>>Service.blk //get command block
blk>>YesNo.type = cmdNo
blk>>YesNo.cause = code
let stat = (ser>>Service.tape)>>TCB.Flags //get tape status
blk>>YesNo.code = stat
YesNoMessage(blk,str,ser) //set up string and send out
]
//---------------------------------------------------------------
and ReplyStatus(ser,cmd) be
//---------------------------------------------------------------
[ //Send back the ending status of the tape drive
// used by most action commands
let blk = ser>>Service.blk
let stat = (ser>>Service.tape)>>TCB.Flags //get tape status
blk>>YesNo.cause = doneOperation
blk>>YesNo.code = stat //return status bits
blk>>YesNo.type = cmdYes //assume Yes
// always no if not online or not ready or error
unless stat<<Status.RDY then blk>>YesNo.type = cmdNo
unless stat<<Status.ONL then blk>>YesNo.type = cmdNo
let error = stat & TapeErr
if error then blk>>YesNo.type = cmdNo
switchon cmd into //other changes to No depend on command
[
case cmdWriteRecord:
[ //No if write protected
if stat<<Status.FPT then blk>>YesNo.type = cmdNo
endcase ]
case cmdFwdSpaceRecord:
[ //No special no
endcase ]
case cmdBackSpaceRecord:
[ //No special no
endcase ]
case cmdFwdSpaceFile:
[ //No special no
endcase ]
case cmdBackSpaceFile:
[ //No special no
endcase ]
case cmdWriteEOF:
[ //No if not EOF
unless stat<<Status.EOF then blk>>YesNo.type = cmdNo
endcase ]
case cmdWriteBlankTape:
[ //No special no
endcase ]
]
//put in string of good or bad operation
let str = "Good Operation"
if blk>>YesNo.type eq cmdNo then str = "Bad Ending Status"
YesNoMessage(blk,str,ser) //set up string and send out
]
//---------------------------------------------------------------
and YesNoMessage(blk,str,ser) be
//---------------------------------------------------------------
[ let i = 0
blk>>YesNo.length = 5 + ((str>>String.length + 2) / 2)
for i = 0 to blk>>YesNo.length - 4 do
[ (lv blk>>YesNo.str)!i = str!i //copy string
]
// and send block out
BSPWriteBlock(ser>>Service.bspStr,blk,0,2*blk>>YesNo.length)
BSPForceOutput(ser>>Service.bspSoc)
]
//---------------------------------------------------------------
and DoErrorProc(str) be
//---------------------------------------------------------------
[
Ws(str)
]