// DDTapeIO.bcpl - Dual Density Tape Low Level Driver
//
// Dual Density Tape Low Level I/O Routines
// Last modified by Tim Diebert, March 27, 1981 11:43 AM
// Copyright Xerox, 1981
//
get "DDTapes.d"
external
[
// Procedures from the ALTO Operating System
Allocate
CallSwat
DefaultArgs
Free
StartIO
sysZone
Zero
// Procedures from the User Program
TapeWaitQuiet // Procedure that waits for other microcode tasks to complete
// Procedures from the User Program or the ALTO Context package
Block
// Procedures from the ALTO Timer Package
InitializeTimer
SetTimer
TimerHasExpired
Dismiss
]
static
[
TapeActive = false
TapeOpened
]
//---------------------------------------------------------------------------------
let ActOnDDTape(vtape, function, rbuf, rcnt, wbuf, wcnt, retries, readafterwrite; numargs nu) = valof
//---------------------------------------------------------------------------------
// Performs operations on tape with retry
// errors are reported to vtape>>VDDTCB.errProc
// operator intervention is reported to vtape>>VDDTCB.interventionProc
//
// Returns:
// controller status on non R/W operations
// byte count on R/W operations
// -1 on Hardware error (timeout)
// -2 on illegal function
// -3 on Not Ready or Offline
// -4 on End of File
// -5 on End of Tape
// -6 on Soft error
// -7 on Other Tape Error
// -8 on File protect error
//
// Calls of interest to the user:
//vtape>>VDDTCB.errProc on error
//vtape>>VDDTCB.interventionProc operator intervention
//
[
DefaultArgs(lv nu, 2, 0, 0, 0, 0, 4, false)
let actualstatus = nil
let tempstatus = nil
let errproc = vtape>>VDDTCB.errProc
let interventionproc = vtape>>VDDTCB.interventionProc
// Check to see if function is invalid
if (function ls 0) % (function gr #37) do
[
errproc("*nActOnTape: Call with Illegal Function")
vtape>>VDDTCB.CMDER = 1
resultis -2
]
// Check and correct number of retries (8 max)
if (retries ls 0) % (retries gr 8) then retries = 8
// Get everything set up for retry
vtape>>VDDTCB.retries = retries
vtape>>VDDTCB.regap = retries
vtape>>VDDTCB.lowthresh = false
vtape>>VDDTCB.operation = function
// check for device On-Line
let currentstatus = PerformVDDTCB(vtape, NoOp)
unless currentstatus do
[
vtape>>VDDTCB.HDWERR = 1
resultis -1
] // return -1 on hung controller
unless currentstatus<<DDStatus.ONL & currentstatus<<DDStatus.RDY then
[
interventionproc("*nTape Drive Offline or Not Ready")
vtape>>VDDTCB.CMDER = 1
resultis -3
]
// PERFORM THE REQUESTED FUNCTION
test (function eq NoOp) % (function eq Rewind) % (function eq Unload)
ifso
[
actualstatus = PerformVDDTCB(vtape, function)
resultis actualstatus
]
ifnot
[
if (((function & #37) & #20) eq #20) & currentstatus<<DDStatus.BOT do
[
errproc("*nActOnTape: Tape is at BOT prior to a REV motion command")
vtape>>VDDTCB.CMDER = 1
resultis -2
]
]
[
[
actualstatus = PerformVDDTCB(vtape, function, rbuf, rcnt, wbuf, wcnt, readafterwrite)
unless actualstatus do
[
vtape>>VDDTCB.HDWERR = 1
resultis -1 // return on hung device
]
if ((#7 & actualstatus) ne 0) do
[
if (actualstatus & #1) ne 0 do
[
errproc("*nActOnTape: Microcode detected Command Error")
vtape>>VDDTCB.CMDER = 1
resultis -2 //ilegal function
]
if (actualstatus & #2) ne 0 do
[
interventionproc("*nTape is Write Protected")
vtape>>VDDTCB.CMDER = 1
resultis -8
]
vtape>>VDDTCB.HDWERR = 1
resultis -1
]
// Check to see if the operation has good ending status or the retry count is used up
let breakout = valof
[
if ((TapeErr & actualstatus) eq 0) % (retries eq 0) % FunctionNotRetryable(function) do resultis true
test vtape>>VDDTCB.WRT
ifnot
[
if vtape>>VDDTCB.lowthresh & (vtape>>VDDTCB.retries le 0) do resultis true
]
ifso
[
if (vtape>>VDDTCB.retries le 0) & (vtape>>VDDTCB.regap le 0) do resultis true
]
resultis false
]
if breakout do break
// retry the operation after backing up, ignoring errors
tempstatus = PerformVDDTCB(vtape, (vtape>>VDDTCB.REV ? FwdSpaceRecord, BackSpaceRecord) )
unless tempstatus do
[
vtape>>VDDTCB.HDWERR = 1
resultis -1
]
vtape>>VDDTCB.retries = vtape>>VDDTCB.retries - 1
// Generate a long wrt gap if the bad operation was a write and the retry count was
// gone
test vtape>>VDDTCB.WRT
ifso
[
if (vtape>>VDDTCB.retries le 0) & (vtape>>VDDTCB.regap le 0) then
[ // Set up to retry the operation a little way farther down on the tape
vtape>>VDDTCB.retries = retries
vtape>>VDDTCB.regap = vtape>>VDDTCB.regap - 1
unless PerformVDDTCB(vtape, Erase) do resultis -1 // Write a longer gap
]
]
ifnot
[
// Use the low threshold if the bad operation was a read op and the retry count was gone
if (vtape>>VDDTCB.retries le 0) & (not vtape>>VDDTCB.lowthresh) then
[ // Set up to retry the operation with low threshold set
vtape>>VDDTCB.retries = retries
vtape>>VDDTCB.lowthresh = true
vtape>>VDDTCB.Threshold = 1
]
]
] repeat
]
// Build the ending status
if vtape>>VDDTCB.FMK then resultis -4
if vtape>>VDDTCB.EOT then resultis -5
if vtape>>VDDTCB.SE do
[
errproc(ErrmsgTapeCB(vtape))
resultis -6
]
if (TapeErr & actualstatus) eq 0 then resultis vtape>>VDDTCB.ByteCount
// The ending status was not good so look up the text for the error and call the error handler
errproc(ErrmsgTapeCB(vtape))
resultis -7
]
//---------------------------------------------------------------------------------
and CloseDDTape(vtape) be
//---------------------------------------------------------------------------------
// Releases the storage for vtape and markes the drive as available in TapeOpened
//
[
if (vtape ne 0) do
[
TapeOpened ! (vtape>>VDDTCB.drive) = false
Free(sysZone,vtape)
]
]
//---------------------------------------------------------------------------------
and EraseInches(vtape, inches) = valof
//---------------------------------------------------------------------------------
// This routine erases "inches" amount of tape (three is a typical figure).
// An "inches" value of zero or less is a no-op, and 72 is the maximum. Any number
// greater than 72 is taken as 72. Inches does not default.
// Note: A standard gap is erased in addition to the erase length specified.
//
//
[
if inches ls 0 then resultis false
if inches gr 72 then inches = 72
resultis PerformVDDTCB(vtape, EraseVar, inches * vtape>>VDDTCB.opDensity)
]
//---------------------------------------------------------------------------------
and FileSkipFwd(vtape) = ActOnDDTape(vtape, FwdSpaceFile)
//---------------------------------------------------------------------------------
// Forward Space File passes over tape records until an EOF or EOT is encountered
//
//---------------------------------------------------------------------------------
and FileSkipRev(vtape)=ActOnDDTape(vtape, BackSpaceFile)
//---------------------------------------------------------------------------------
// Reverse Space File passes over tape records in the reverse direction until EOF or BOT
//---------------------------------------------------------------------------------
and OpenDDTape(unit, errproc, interventionproc; numargs nu) = valof
//---------------------------------------------------------------------------------
// Allocates a VDDTCB area for the unit specified, sets up the pointer to the errProc.
//
// Returns:
// 0 if Open was Unsuccessful
// pointer to the VDDTCB if Successful
//
// Calls of interest to the user:
// errproc
//
[
DefaultArgs(lv nu, 0, 0, CallSwat, CallSwat)
// Check for Valid Tape Unit Number
if (unit ls 0) % (unit gr MaxUnitNum ) do
[
errproc("*nIllegal Tape Unit Number")
resultis 0
]
// Allocate some space for the VDDTCB
let vtape = Allocate(sysZone, lVDDTCB, true, true)
unless vtape do
[
errproc("*nSystem Error: Unable to allocate space for VDDTCB")
resultis 0
]
Zero(vtape, lVDDTCB)
// Check to see if unit is already opened.
if TapeOpened ! unit do
[
errproc("*nOpen command issued on tape unit which was already open.")
Free(sysZone, vtape)
resultis 0
]
// Set up areas in the VDDTCB that relate to the drive we just opened.
vtape>>VDDTCB.drive = unit
vtape>>VDDTCB.errProc = errproc
vtape>>VDDTCB.interventionProc = interventionproc
vtape>>VDDTCB.Command = 0
vtape>>VDDTCB.opDensity = PE
// Indicate the drive is now opened in TapeOpened
TapeOpened ! unit = true
// Initalize everything
let stat = PerformVDDTCB(vtape, NoOp)
if vtape>>VDDTCB.NRZI then vtape>>VDDTCB.opDensity = NRZI
resultis vtape
]
//---------------------------------------------------------------------------------
and PerformVDDTCB(vtape, function, readbuf, readcount, writebuf, writecount,
readafterwrite; numargs nu) = valof
//---------------------------------------------------------------------------------
// Sets up and executes a TCB located inside the VDDTCB area to perform the
// requested funtion.
//
// Returns:
// 0 on tape operation timeout
// hardware status if no timeout
//
// Calls of interest to the user:
// vtape>>VDDTCB.errProc on any error execpt an attempt to operate
// on a drive that has not been opened, in which case CallSwat is executed.
//
[
DefaultArgs(lv nu, 2, 0, 0, 0, 0, false)
// Is vtape allocated?
unless vtape do
[
CallSwat("*nTapeIO: PerformVDDTCB called without a VDDTCB being allocated")
resultis 0
]
// Is the drive open?
unless TapeOpened ! (vtape>>VDDTCB.drive) do
[
CallSwat("*nTapeIO: PerformVDDTCB called without a drive being Opened")
resultis 0
]
// Check for reset.
if function eq Reset do
[
Zero(vtape, lDDTCB)
vtape>>VDDTCB.Unit = 0
vtape>>VDDTCB.Command = 0
vtape>>DDTCB.Density = 0 // assume PE mode
if vtape>>VDDTCB.opDensity eq NRZI do
vtape>>DDTCB.Density = 1
vtape>>DDTCB.Opcode = NoOp
@DDTCBStart=vtape
StartIO(TapeResetOp)
// DO the reset
Dismiss(4)
resultis vtape>>DDTCB.Flags
]
// set up the hardware TCB
let opcode = nil
Zero(vtape, lDDTCB)
vtape>>DDTCB.WriteCount = writecount
vtape>>DDTCB.WriteBuffer = writebuf
vtape>>DDTCB.ReadCount = readcount
vtape>>DDTCB.ReadBuffer = readbuf
vtape>>DDTCB.Unit = vtape>>VDDTCB.drive
if vtape>>VDDTCB.lowthresh then vtape>>DDTCB.Threshold = 1
if readafterwrite then vtape>>DDTCB.ReadAfterWrite = 1
vtape>>DDTCB.Density = 0 // assume PE mode
if vtape>>VDDTCB.opDensity eq NRZI do
vtape>>DDTCB.Density = 1
// ERASE functions takes 3rd parameter as write byte count for erase timing
if function eq EraseVar do
vtape>>DDTCB.WriteCount = readbuf
// PerformVDDTCB (cont’d)
// Set up opcode
vtape>>DDTCB.Opcode = (function & #37)
// WAIT FOR ANY PENDING TRIDENT OR TAPE I/O FOR BANDWIDTH’S SAKE
TapeWaitQuiet()
while TapeActive do Block()
TapeActive = true
// Check to see if we will transfer data so we can turn off the display
let DispOffFlag = false
let DCBStartSave = nil
switchon function into
[
case ReadFwd:
case ReadRev:
case ReadRevEdit:
case Write:
case WriteEdit:
case EraseVar:
[
if @DCBStart ne 0 do
[
DispOffFlag = true
DCBStartSave = @DCBStart
@DCBStart = 0
DDTapeVertIntFlag = false
until DDTapeVertIntFlag do Block()
]
]
endcase
]
// start the instruction
@DDTCBStart = vtape
StartIO(TapeNormalOp)
unless function eq Unload do WaitforTapeStatus(vtape, function)
if DispOffFlag do @DCBStart = DCBStartSave
TapeActive = false
// return with the controller status
resultis vtape>>DDTCB.Flags
]
//---------------------------------------------------------------------------------
and RecSkipFwd(vtape) = ActOnDDTape(vtape, FwdSpaceRecord)
//---------------------------------------------------------------------------------
// Skips one record forward
//---------------------------------------------------------------------------------
and RecSkipRev(vtape) = ActOnDDTape(vtape, BackSpaceRecord)
//---------------------------------------------------------------------------------
// Skips one record reverse
//---------------------------------------------------------------------------------
and SetDensity(vtape, density) = valof
//---------------------------------------------------------------------------------
// Sets the tape density.
[
// Check for VDDTCB allocated
unless vtape do
[
CallSwat("*nTapeIO: No VDDTCB allocated")
resultis false
]
let errproc = vtape>>VDDTCB.errProc
// Check for Valid Density Selection
unless (density eq PE) % (density eq NRZI) do
[
errproc("*nSpecified Tape Density Not Supported")
resultis false
]
let flags = PerformVDDTCB(vtape, NoOp)
unless flags do resultis false
let DensitySameFlag = false
test flags<<DDStatus.NRZI
ifso
if (density eq NRZI) do
[
DensitySameFlag = true
]
ifnot
if (density eq PE) do
[
DensitySameFlag = true
]
if flags<<DDStatus.BOT do
[
vtape>>VDDTCB.opDensity = density
resultis true
]
if (not vtape>>VDDTCB.BOT) & DensitySameFlag do resultis true
resultis false
]
//---------------------------------------------------------------------------------
and TapeEOF(vtape) = ActOnDDTape(vtape, WriteEOF)
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
and TapeStatus(vtape) = PerformVDDTCB(vtape, NoOp)
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
and TapeRewind(vtape) = PerformVDDTCB(vtape, Rewind)
//---------------------------------------------------------------------------------
// Rewinds the tape
//---------------------------------------------------------------------------------
and TapeRewindWait(vtape) = valof
//---------------------------------------------------------------------------------
// Rewinds the tape
//
// Returns:
// true if the rewind completed
// false if the rewind timeout
//
// Calls of interest to the user:
// vtape>>VDDTCB.errProc on error (via WaitTapeReady)
//
[
PerformVDDTCB(vtape, Rewind)
let tempstatus = WaitTapeReady(vtape)
resultis tempstatus
]
//---------------------------------------------------------------------------------
and TapeUnload(vtape) be
//---------------------------------------------------------------------------------
[
TapeRewindWait(vtape)
PerformVDDTCB(vtape, Unload)
]
//---------------------------------------------------------------------------------
and WaitTapeReady(vtape) = valof
//---------------------------------------------------------------------------------
// Waits for the drive to report ready
//
// Returns:
// true if drive came ready before the timeout
// false if timeout
//
[
let tempstatus = nil
let Timer = nil
SetTimer(lv Timer, RewindTimeout) // Timer for rewind time 2400’ @ 150 ips
[
Block()
tempstatus = PerformVDDTCB(vtape, NoOp)
] repeatuntil tempstatus<<DDStatus.RDY % TimerHasExpired(lv Timer)
unless tempstatus<<DDStatus.RDY resultis false
resultis true
]
// Local Procedures to help make life easier
//---------------------------------------------------------------------------------
and ErrmsgTapeCB(vtape) = valof
//---------------------------------------------------------------------------------
// formats an error message that summarizes the controller status
//
// Returns:
// text line describing error
//
[
if vtape>>VDDTCB.HE then resultis "*n Ending Status Bad HE"
if vtape>>VDDTCB.SE then resultis "*n Ending Status Bad SE"
if vtape>>VDDTCB.DL then resultis "*n Ending Status Bad DL"
if vtape>>VDDTCB.RDP then resultis "*n Ending Status Bad RDP"
if vtape>>VDDTCB.ICL then resultis "*n Ending Status Bad ICL"
if vtape>>VDDTCB.HDWERR then resultis "*n Ending Status Bad HDWERR"
if vtape>>VDDTCB.WFP then resultis "*n Ending Status Bad WFP"
if vtape>>VDDTCB.CMDER then resultis "*n Ending Status Bad CMDER"
// Error messages in order of precedence
resultis "*n NOT OK"
]
//---------------------------------------------------------------------------------
and FunctionNotRetryable(function) = valof
//---------------------------------------------------------------------------------
// test for not retryable function codes
//
// Returns:
// true if not retryable
//
[
if (function eq BackSpaceFile) % (function eq FwdSpaceFile) % (function eq Erase) % (function eq EraseVar) % (function eq Reset) resultis true
resultis false
]
//---------------------------------------------------------------------------------
and WaitforTapeStatus(vtape, function) be
//---------------------------------------------------------------------------------
// Causes a wait for vtape>>TCB.Flags ne 0 or a {3, 1} sec timeout at {45, 125} IPS
//
// Returns:
// vtape>>TCB.Flags = 0 if Timeout
// vtape>>TCB.Flags = Hardware DDStatus if not Timeout
//
// Calls:
// vtape>>VDDTCB.errProc on Timeout.
//
[
TapeActive = true
vtape>>VDDTCB.timeout = false
// Wait for the required time.
let Timer = nil
let alarmtime = RecordTimeout
if (function eq FwdSpaceFile) % (function eq BackSpaceFile) do alarmtime = FileTimeout
if (function eq NoOp) do alarmtime = NoOpTimeout
SetTimer(lv Timer, alarmtime)
until (vtape>>DDTCB.Flags ne 0) % TimerHasExpired(lv Timer) do Block()
// Done waiting. If the status from the controller is still zeros, the timer expired.
if vtape>>DDTCB.Flags eq 0 do
[
vtape>>VDDTCB.timeout = true
let errproc = vtape>>VDDTCB.errProc
// Timer expired: Reset the microcode and wait for the controller to settle.
vtape>>DDTCB.Opcode = NoOp
@DDTCBStart=vtape
StartIO(TapeResetOp)
Dismiss(4)
// Check to see if operation was a NOP
test (function eq NoOp) % (function eq Reset)
ifso
[ // Timeout on a NOP indicates a hardware problem
errproc("*nTape Controller Timeout on NoOp; Possible Hardware Problem")
vtape>>DDTCB.Flags = 0
]
ifnot
[
test vtape>>DDTCB.BOT
ifso
[ // Timeout on a motion command @ BOT indicates a hardware problem
errproc("*nTape Controller Timeout at BOT; Possible Hardware Problem")
vtape>>DDTCB.Flags = 0
]
ifnot
[ // Timeout on a motion command not @ BOT may indicate a blank tape
errproc("*nTape Controller Timeout; Possible Blank Tape")
vtape>>DDTCB.Flags = 0
]
]
]
TapeActive = false
]