SA800HeadDLion.mesa
last modified August 26, 1982 4:43 pm by Taft
Things to do:
1. One and only one drive is handled by this Head. The Dandelion will
have exactly one SA80x or SA85x type drive and exactly one floppy disk
controller.
2. The operation.buffer.length is ignored.
It should be removed from the face.
DIRECTORY
DeviceCleanup USING [Await, Item, Reason],
DLionInputOutput USING [IOPage],
Environment USING [Base],
HeadStartChain USING [Start],
Inline USING [BITAND, BITOR, DIVMOD, LongCOPY, LowHalf],
SA800Face;
SA800HeadDLion: PROGRAM
IMPORTS
DLionInputOutput, DeviceCleanup, RemainingHeads: HeadStartChain, Inline
EXPORTS HeadStartChain, SA800Face
SHARES SA800Face =
BEGIN OPEN SA800Face;
***********************************************************************
C S B
The Dandelion has at most one Shugart SA80x or SA85x
floppy disk. The CSB resides in a resident page of memory (the IOPage).
Only state.next and .needReset are known by IOP code.
***********************************************************************
CSB: TYPE = MACHINE DEPENDENT RECORD [
transferMask(0): WORD, -- naked notify mask.
next(1): IOPBlockPtr, -- IOCB executed when Output is done.
unused0(2): UNSPECIFIED,
needReset(3): BOOLEAN, -- TRUE => reset required.
tail(4:0..31): IOCBPtr]; -- Last IOCB in queue. Used only by Initiate.
unused1(6: 0..31): UNSPECIFIED];
csb: LONG POINTER TO CSB = LOOPHOLE[DLionInputOutput.IOPage + 111B]; -- 49'X
***********************************************************************
I O C B (and IOP command blocks)
***********************************************************************
IOCBPtr: TYPE = LONG POINTER TO IOCB;
IOPBlockPtr: TYPE = Environment.Base RELATIVE POINTER TO IOPBlock;
nilIOP: IOPBlockPtr = LOOPHOLE[0];
IOCB: TYPE = MACHINE DEPENDENT RECORD [
operation(0): Operation, -- set by head client
iopBlk(8): IOPBlock, -- read/written by IOCB
started(24:0..0): BOOLEAN, -- => IOCB has been submitted to IOP
thisRun(24:1..15): [0..77777B], -- pages/tracks this trip
abortStatus(25): Status, -- saved status. An operation may be aborted
after it passes Initiate (new Disk Change while it is in the queue). This
saves the status so the user finds out why it failed.
nextIOCB(26): IOCBPtr]; -- points to next IOCB in chain
IOPBlock: TYPE = MACHINE DEPENDENT RECORD [
bufferAddr(0:0..31): LONG POINTER, -- address disk buffer (unused if format)
unused(2): UNSPECIFIED ← 0,
sectorLength(3): CARDINAL, -- sector length(bytes)(unused if format)
sectorFormat(4): FormatWord, -- sector Format
diskAddress(5:0..31): DiskAddress, -- cylinder, head and sector
sectorCount(7): CARDINAL, -- sectors to transfer <= sectorPerTrack
(if format, track count)
result(8): IOPResult, -- status posted by IOP
samePage(9:0..0): BOOLEAN, -- TRUE => use same memory page for
all sectors (unused if format)
command(9:1..15): Command, -- IOP command word
subCommand(10): SubCommand, -- IOP sub-command word (IOP=esc Only)
formatInfo(11:0..31): FormattingInfo, -- used only in format
reserved(13:0..47): ARRAY [0..3) OF UNSPECIFIED];
FormattingInfo: TYPE = MACHINE DEPENDENT RECORD [
sectorLengthDiv4(0:0..7): CARDINAL [0..400B), -- sector length
((bytes)/4) MOD 256
encodedSectorLength(0:8..15): EncodedSectorLength, -- encoded sector length
sectorsPerTrack(1:0..7): CARDINAL [0..400B),
gap3FillCharsCount(1:8..15): CARDINAL [0..400B)]; -- interrecord gap count
order of these guys is significant to Head code as well as IOP
Command: TYPE = MACHINE DEPENDENT {
nopC(0), readSectorC, writeSectorC, writeDeletedSectorC, readIDC, formatTrackC,
recalibrateC, initializeC, escapeC, (77777B)};
SubCommand: TYPE = MACHINE DEPENDENT {nopSC(0), diskChangeClearSC, (177777B)};
EncodedSectorLength: TYPE = MACHINE DEPENDENT {
b128(0), b256(1), b512(2), b1024(3), bUnknown};
FormatWord: TYPE = MACHINE DEPENDENT RECORD [
troyOrIBM(0:0..11): MACHINE DEPENDENT {IBM(0), Troy(1), (7777B)},
density(0:12..15): MACHINE DEPENDENT {single(0), double(10B), (17B)}];
IOPResult: TYPE = MACHINE DEPENDENT RECORD [
doorOpened(0:0..0): BOOLEAN, -- TRUE => operator opened disk door
latched until
DiskChangeClear[] AND ~notReady
unused1(0:1..1): BOOLEANFALSE,
twoSided(0:2..2): BOOLEAN, -- true => two sided diskette installed
diskID(0:3..3): MACHINE DEPENDENT {SA800(0), SA850(1)},
error(0:4..4): BOOLEAN, -- IOP detected an error in the IOCB
unused2(0:5..5): BOOLEANFALSE,
recalibrateError(0:6..6): BOOLEAN, -- IOP had error during recalibrate op
dataLost(0:7..7): BOOLEAN, -- data transfer not completed
notReady(0:8..8): BOOLEAN, -- drive has door open
writeProtect(0:9..9): BOOLEAN, -- write-protected diskette now in drive
deletedData(0:10..10): BOOLEAN, -- read a deleted data sector
recordNotFound(0:11..11): BOOLEAN, -- some sector in transfer not found
crcError(0:12..12): BOOLEAN, -- CRC error on read op
track00(0:13..13): BOOLEAN, -- read heads left on cylinder 0
unused3(0:14..14): BOOLEANFALSE,
busy(0:15..15): BOOLEAN]; -- => chip busy, should always be FALSE
***********************************************************************
C O N S T A N T S
(canned stati can be found at the end of the module (so they won't lexically
pollute code readers)
***********************************************************************
commandMatrix: ARRAY Function OF Command = [ -- face => IOP command
nopC, recalibrateC, escapeC, readSectorC, writeSectorC, writeDeletedSectorC,
readIDC, formatTrackC];
nullIOCB: IOCB = [
operation: [
device: LOOPHOLE[0], function: nop, incrementDataPointer: FALSE, count: 0,
address: [cylinder: 0, head: 0, sector: 0], buffer: [address: NIL, length: 0]],
iopBlk: nullIOPBlk, started: FALSE, thisRun: 0,
abortStatus: initialStatus, nextIOCB: NIL];
nullIOPBlk: IOPBlock =[
bufferAddr: NIL,
unused: 0,
sectorLength: 0,
sectorFormat: [troyOrIBM: IBM, density: single],
diskAddress: [cylinder: 0, head: 0, sector: 0],
sectorCount: 0,
result: nullIOPResult,
samePage: FALSE, command: nopC, subCommand: nopSC,
formatInfo: [0, b128, 0, 0],
reserved: [0, 0, 0]];
nullIOPResult: IOPResult = [
doorOpened: FALSE, twoSided: FALSE, diskID: SA800, error: FALSE,
recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE,
writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE,
crcError: FALSE, track00: FALSE, busy: FALSE];
nullDeviceHandle: PUBLIC DeviceHandle ← LOOPHOLE[0];
diskDeviceHandle: DeviceHandle = LOOPHOLE[1];
internalDeviceHandle: DeviceHandle = LOOPHOLE[2]; -- for internally generated IOCBs.
trackParameterLength: CARDINAL = 0; -- space needed for FormatTrack parameters
idLength: CARDINAL = 3; -- length of field ID in words
numOfCylinders: CARDINAL = 77; -- number of cylinders on SA800 and SA850 disks
initialAllocationLength: PUBLIC CARDINALSIZE[IOCB];
operationBlockLength: PUBLIC CARDINALSIZE[IOCB];
***********************************************************************
G L O B A L V A R I A B L E S
***********************************************************************
initializationIOCB: IOCBPtr;
state: RECORD [
user: Context --user accessable state
← [protect: FALSE, format: IBM, density: double, sectorLength: 256],
status: Status ← diskChange, --most currently processed status
result: IOPResult ← nullIOPResult, --most currently processed IOP result
sectorsPerTrack: CARDINAL ← 15, --function[sectorLength, density]
encodedSectorLength: EncodedSectorLength ← b512];--function[sectorLength]
do not submit an IOCB if there is nothing to do. This also eliminates
the possiblilty of interrupting an existing IOCB. Since the occurence of
a new diskChange error causes the IOCB queue to be cleared, the IOCB about
to be submitted will arrive at the Head of the queue so it may be Polled
immediately.
DiskChangeClear: PUBLIC PROCEDURE [DeviceHandle] =
BEGIN
ready: BOOLEAN;
IF ~state.status.diskChange THEN RETURN;
state.status.diskChange ← FALSE;
ready ← ~Immediate[escapeC, diskChangeClearSC, nop].notReady;
if there is a diskette inserted (notReady=FALSE) then recalibrate
so the FDC and IOP are synchronized. When the heads are recalibrated they
they are loaded => door lock engaged. If there were no disk, the FDC would
never see index pulses and never ever unload the heads, leaving the door locked
which is mildly embarrassing. This may also be the first recalibrate if there was no
disk loaded when the system was initialized.
IF ready THEN [] ← Immediate[recalibrateC, nopSC, recalibrate]
END;
Assumes an "Initialize" operation has been performed so "state.status"
holds valid state bits
GetDeviceAttributes: PUBLIC PROC [DeviceHandle] RETURNS [Attributes] =
BEGIN
RETURN[[
deviceType: IF state.result.diskID=SA800 THEN SA800 ELSE SA850,
numberOfHeads: IF ~state.status.twoSided THEN 1 ELSE 2,
numberOfCylinders: numOfCylinders, trackLength: trackParameterLength]];
END;
ASSUMES EXACTLY ONE FLOPPY DRIVE IS CONNECTED TO THE DANDELION
GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = {
RETURN[IF device = nullDeviceHandle THEN diskDeviceHandle ELSE nullDeviceHandle]};
GetContext: PUBLIC PROC [DeviceHandle] RETURNS [Context] = {RETURN[state.user]};
This code will only handles a single controller/single device configuration.
Initialize: PUBLIC PROC [notify: WORD, initialAllocation: LONG POINTER] =
BEGIN
csb^ ← [
transferMask: notify, next: nilIOP, unused0: NULL,
needReset: FALSE, tail: NIL];
initializationIOCB ← initialAllocation;
initializationIOCB^ ← nullIOCB;
initializationIOCB.operation.device ← internalDeviceHandle;
[] ← Immediate[initializeC, nopSC, nop];
state.status.diskChange ← TRUE;
END;
InitializeCleanup: PUBLIC PROC [DeviceHandle] =
BEGIN
item: DeviceCleanup.Item;
sCsb: CSB;
DO
reason: DeviceCleanup.Reason = DeviceCleanup.Await[@item];
SELECT reason FROM
turnOff, kill =>
{WHILE csb.next#nilIOP DO ENDLOOP; sCsb ← csb^};
turnOn => csb^ ← sCsb;
ENDCASE
ENDLOOP
END;
This procedure initiates an operation. The client passes an IOCB with most of the parameters filled in. We fill in the parameters that are unique to this implementation of the Head and microcode. Then we chain the IOCB onto the CSB an wake up the microcode. The variable maxSecondsThisOperation may be used by the driver to timeout and rescue the operation. It is an upper bound on the number of seconds the operation will take after previous operations have been completed.
Initiate: PUBLIC PROC [o: OperationPtr]
RETURNS [status: Status, maxSecondsThisOperation: CARDINAL] =
BEGIN OPEN iocb: LOOPHOLE[o, IOCBPtr];
maxSecondsThisOperation ← 0;
IF o.device # diskDeviceHandle AND o.device # internalDeviceHandle THEN
RETURN[Inline.BITOR[statusError, Inline.BITAND[infoMask, state.status]], 0];
IF o.device = diskDeviceHandle THEN iocb.iopBlk ← nullIOPBlk;
iocb.nextIOCB ← NIL; iocb.started ← FALSE; iocb.abortStatus ← inProgress;
IF (status ← CheckIOCB[@iocb]).inProgress THEN
BEGIN
IF csb.next = nilIOP THEN RunIOCB[@iocb] ELSE csb.tail.nextIOCB ← @iocb;
csb.tail ← @iocb;
maxSecondsThisOperation ← SELECT o.function FROM
readSector, writeSector, writeDeletedSector, readID =>
20 + (o.count + iocb.iopBlk.sectorCount)/5,
formatTrack => 20 + (o.count + iocb.iopBlk.sectorCount),
nop, recalibrate => 10,
ENDCASE => 0;
END
ELSE iocb.started ← TRUE; -- return (error) status if somebody polls this guy
END;
Poll: PUBLIC PROC [o: OperationPtr] RETURNS [status: Status] =
BEGIN OPEN iocb: LOOPHOLE[o, IOCBPtr];
IF ~iocb.abortStatus.inProgress THEN RETURN[iocb.abortStatus];
IF csb.next#nilIOP OR ~iocb.started THEN
RETURN[Inline.BITOR[inProgress, Inline.BITAND[state.status, infoMask]]];
IF (status ← GetStatus[@iocb]).inProgress THEN RunIOCB[@iocb]
ELSE
FOR newIOCB: IOCBPtr ← iocb.nextIOCB, newIOCB.nextIOCB WHILE newIOCB # NIL DO
newIOCB.started ← TRUE;
IF CheckIOCB[newIOCB].inProgress THEN {RunIOCB[newIOCB]; EXIT};
ENDLOOP;
END;
Reset: PUBLIC PROC [DeviceHandle] =
{csb.next ← nilIOP; csb.needReset ← TRUE; csb.tail ← NIL};
This routine requires that the device be idle (i.e., the IOCB chain to be NIL)
as well as all other context to be correct before anything is changed in the
head's context.
SetContext: PUBLIC PROC [device: DeviceHandle, context: Context] RETURNS [BOOLEAN] =
BEGIN
Density: TYPE = {single, double};
sectorsPerTrack: ARRAY EncodedSectorLength[b128..b1024] OF ARRAY Density OF CARDINAL
= [ -- Single density Double Density
-- 64w-- [26, 36],
-- 128w-- [15, 26],
-- 256w-- [ 8, 15],
-- 512w-- [ 4, 8]];
byteIndex: EncodedSectorLength = SELECT context.sectorLength FROM
64 => b128, 128 => b256, 256 => b512, 512 => b1024, ENDCASE => bUnknown;
IF (byteIndex=bUnknown) OR (csb.next#nilIOP) OR (context.format#IBM) THEN
RETURN[FALSE];
state.user ← context;
state.sectorsPerTrack
← sectorsPerTrack[byteIndex][IF context.density=single THEN single ELSE double];
state.encodedSectorLength ← byteIndex;
RETURN[TRUE];
END;
exported to HeadStartChain
Start: PUBLIC PROC = {RemainingHeads.Start[]};
************************** PRIVATE PROCEDURES **************************
BuildIOCB: PROCEDURE [iocb: IOCBPtr] =
BEGIN OPEN o: iocb.operation;
b: LONG POINTER TO IOPBlock = @iocb.iopBlk;
runLength: CARDINAL;
IF o.device = internalDeviceHandle THEN RETURN;
b.result ← nullIOPResult;
IF state.status.notReady THEN {b.command ← nopC; RETURN};
b.command ← commandMatrix[o.function];
SELECT o.function FROM
nop, recalibrate => NULL; -- allSetUp
recovery => b.command ← nopC; -- for now, do nothing
readSector, writeSector, writeDeletedSector, readID =>
BEGIN OPEN a: o.address;
runLength ← MIN[o.count, state.sectorsPerTrack-(a.sector-1)];
Touch[
addr: o.buffer.address,
count:
(IF o.function=readID THEN idLength ELSE state.user.sectorLength)
* (IF o.incrementDataPointer THEN runLength ELSE 1),
dirty: (o.function=readSector OR o.function=readID)];
b.samePage ← ~o.incrementDataPointer;
b.bufferAddr ← o.buffer.address;
b.sectorLength ← state.user.sectorLength;
b.sectorFormat ← [
troyOrIBM: IF state.user.format = Troy THEN Troy ELSE IBM,
density: IF state.user.density = double THEN double ELSE single];
b.diskAddress ← o.address; -- set first disk address
b.sectorCount ← runLength; -- set number of sectors to transfer
iocb.thisRun ← runLength; -- and set run length for getStatus fixup.
END;
formatTrack =>
BEGIN OPEN a: o.address;
Density: TYPE = {single, double};
gap3: ARRAY EncodedSectorLength [b128..b1024] OF ARRAY Density OF CARDINAL
[512w][single] and [64w][double] are picked out of the air
= [ -- Single density Double Density
-- 64w-- [27, 26],
-- 128w-- [42, 54],
-- 256w-- [58, 84],
-- 512w-- [75, 116]];
b.formatInfo ← [
sectorLengthDiv4: state.user.sectorLength/2, -- (x * 2 bytes/wd)/4
encodedSectorLength: state.encodedSectorLength,
sectorsPerTrack: state.sectorsPerTrack,
gap3FillCharsCount:
gap3[state.encodedSectorLength]
[IF state.user.density=single THEN single ELSE double]];
runLength ← MIN[
o.count, IF state.status.twoSided THEN 1 ELSE (numOfCylinders - a.cylinder)];
b.sectorFormat ← [
troyOrIBM: IF state.user.format = Troy THEN Troy ELSE IBM,
density: IF state.user.density = double THEN double ELSE single];
b.diskAddress ← o.address;
b.sectorCount ← runLength;
iocb.thisRun ← runLength;
END
ENDCASE;
END;
a procedure to check an IOCB before it is submitted to the IOP. It attempts
to screen out IOCBs which shouldn't even be attempted
CheckIOCB: PROCEDURE [iocb: IOCBPtr] RETURNS [status: Status] =
BEGIN OPEN a: iocb.operation.address, o: iocb.operation, Inline;
IsWriteOp: PROC [f: Function] RETURNS [BOOLEAN] =
INLINE {RETURN[f=writeSector OR f=writeDeletedSector OR f=formatTrack]};
IF ~iocb.abortStatus.inProgress THEN RETURN[iocb.abortStatus];
status ← inProgress;
IF o.function = formatTrack THEN a.sector ← 1;
SELECT TRUE FROM
o.device = internalDeviceHandle => NULL;
state.status.diskChange => status ← diskChange;
(IsWriteOp[o.function]
AND (state.user.protect OR state.status.writeProtect)) =>
status ← writeProtect;
(IsTransferOp[o.function] AND
(a.cylinder >= numOfCylinders
OR a.head > (IF state.status.twoSided THEN 1 ELSE 0)
OR a.sector NOT IN [1..state.sectorsPerTrack]
OR o.count = 0)) => status ← recordNotFound;
ENDCASE => NULL;
iocb.abortStatus ← status ← BITOR[status, BITAND[infoMask, state.status]];
END;
a procedure to produce the status of an IOCB after it has been completed by the IOP.
In addition to deciding if the stage just performed had any errors, this procedure
decides if there are any more stages to be performed. NOTE: there is no reason to
detect abortStatus.inProgress=FALSE since this routine is only called from Poll
which will detect this condition before trying to call GetStatus.
GetStatus: PROCEDURE [iocb: IOCBPtr] RETURNS [status: Status] =
BEGIN OPEN b: iocb.iopBlk, o: iocb.operation;
command: Command = b.command;
result: IOPResult ← b.result; -- done merely for better code
pResult: POINTER TO READONLY IOPResult = @result;
update disk addr and client buffer parameters
BEGIN OPEN a: o.address;
countDone: CARDINAL ← (iocb.thisRun - b.sectorCount);
o.count ← o.count - countDone;
SELECT command FROM
formatTrackC =>
IF countDone#0 THEN SELECT TRUE FROM
if twoSided, countDone can must <= 1
b.result.twoSided AND (a.head = 0) => a.head ← 1;
ENDCASE => {a.head ← 0; a.cylinder ← a.cylinder + countDone};
readSectorC, readIDC, writeSectorC, writeDeletedSectorC =>
BEGIN
IF o.incrementDataPointer THEN
BEGIN
length: CARDINAL =
(IF command=readIDC THEN idLength ELSE state.user.sectorLength) * countDone;
o.buffer.length ← o.buffer.length - length;
o.buffer.address ← o.buffer.address + length;
END;
[quotient: countDone, remainder: a.sector]
← Inline.DIVMOD[(a.sector-1) + countDone, state.sectorsPerTrack];
a.sector ← a.sector + 1;
since we break runs at tracks, countDone <= 1
IF countDone#0 THEN SELECT TRUE FROM
b.result.twoSided AND (a.head = 0) => a.head ← 1;
ENDCASE => {a.head ← 0; a.cylinder ← a.cylinder + countDone};
END;
ENDCASE;
END;
status ← initialStatus; -- ← ALL[FALSE];
status.diskChange ←
state.status.diskChange
OR (state.status.notReady AND ~pResult.notReady)
OR (~state.status.notReady AND (pResult.notReady OR pResult.doorOpened));
status.twoSided ← pResult.twoSided;
status.na3 ← FALSE;
(← TRUE) SA800 since Mokelumne pilot thinks this is a hardware error bit
status.notReady ← pResult.notReady;
status.writeProtect ← state.user.protect OR pResult.writeProtect;
status.na1 ← FALSE;
status.index ← FALSE; ++ IOP never sends an index pulse back
SELECT command FROM
readSectorC =>
BEGIN
status.deletedData ← pResult.deletedData;
status.crcError ← pResult.crcError;
status.dataLost ← pResult.dataLost;
status.recordNotFound ←
pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0);
END;
IN [writeSectorC..readIDC] =>
BEGIN
status.crcError ← pResult.crcError;
status.dataLost ← pResult.dataLost;
status.recordNotFound ←
pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0);
END;
formatTrackC =>
BEGIN
status.dataLost ← pResult.dataLost;
status.recordNotFound ←
pResult.recordNotFound OR (o.address.cylinder >= numOfCylinders AND o.count > 0);
END;
recalibrateC =>
BEGIN
status.recalibrateError ← pResult.recalibrateError;
status.track00 ← pResult.track00;
END;
initializeC, escapeC =>
status.track00 ← pResult.track00;
ENDCASE;
set error flag if there was an error or the run proceeds off the end of the disk.
status.error ← Inline.BITAND[status, statusError] # 0;
status.inProgress ← ~status.error AND IsTransferOp[o.function] AND o.count > 0;
status.busy ← status.inProgress OR (iocb.nextIOCB # NIL);
state.status ← iocb.abortStatus ← status;
END;
Immediate: PROCEDURE [
command: Command, subCommand: SubCommand, function: Function]
RETURNS [initStatus: Status] =
BEGIN
initializationIOCB.iopBlk.command ← command;
initializationIOCB.iopBlk.subCommand ← subCommand;
initializationIOCB.operation.function ← function;
initializationIOCB.operation.device ← internalDeviceHandle;
initStatus ← Initiate[@initializationIOCB.operation].status;
WHILE initStatus.inProgress DO
initStatus ← Poll[@initializationIOCB.operation] ENDLOOP
END;
IsTransferOp: PROC [f: Function] RETURNS [b: BOOLEAN] =
--INLINE-- BEGIN
b ← SELECT f FROM
readSector, readID, writeSector, writeDeletedSector, formatTrack => TRUE,
ENDCASE => FALSE;
END;
RunIOCB: PROC [iocb: IOCBPtr] =
BEGIN
BuildIOCB[iocb];
iocb.started ← TRUE;
IF csb.next=nilIOP THEN csb.next ← LOOPHOLE[Inline.LowHalf[@iocb.iopBlk]];
END;
change this to use checksum when implemented in microcode
Touch: PROC [addr: LONG POINTER, count: CARDINAL, dirty: BOOLEAN] =
BEGIN
IF dirty THEN
BEGIN
next: LONG POINTER = addr+1;
IF count#0 THEN
{addr^ ← 0; Inline.LongCOPY[from:addr, to: next, nwords: count-1]}
END
ELSE THROUGH [0..count) DO dummy: CARDINAL ← addr^; addr ← addr+1 ENDLOOP;
END;
***********************************************************************
Various B O R I N G C O N S T A N T S
***********************************************************************
recordNotFound: Status =
[diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE,
inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE,
notReady: FALSE, writeProtect: FALSE, deletedData: FALSE,
recordNotFound: TRUE, crcError: FALSE, track00: FALSE, index: FALSE,
busy: FALSE];
notReady: Status =
[diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE,
inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE, notReady: TRUE,
writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE,
crcError: FALSE, track00: FALSE, index: FALSE, busy: FALSE];
diskChange: Status =
[diskChange: TRUE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE,
inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE,
notReady: FALSE, writeProtect: FALSE, deletedData: FALSE,
recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE,
busy: FALSE];
initialStatus: Status = LOOPHOLE[0]; -- ALL[FALSE]
inProgress: Status =
[diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: FALSE,
inProgress: TRUE, recalibrateError: FALSE, dataLost: FALSE, notReady: FALSE,
writeProtect: FALSE, deletedData: FALSE, recordNotFound: FALSE,
crcError: FALSE, track00: FALSE, index: FALSE, busy: TRUE];
writeProtect: Status =
[diskChange: FALSE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE,
inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE,
notReady: FALSE, writeProtect: TRUE, deletedData: FALSE,
recordNotFound: FALSE, crcError: FALSE, track00: FALSE, index: FALSE,
busy: FALSE];
statusError: Status =
[diskChange: TRUE, na1: FALSE, twoSided: FALSE, na3: FALSE, error: TRUE,
inProgress: FALSE, recalibrateError: TRUE, dataLost: TRUE, notReady: TRUE,
writeProtect: FALSE, deletedData: FALSE, recordNotFound: TRUE,
crcError: TRUE, track00: FALSE, index: FALSE, busy: FALSE];
infoMask: Status =
[diskChange: FALSE, na1: FALSE, twoSided: TRUE, na3: FALSE, error: FALSE,
inProgress: FALSE, recalibrateError: FALSE, dataLost: FALSE,
notReady: FALSE, writeProtect: TRUE, deletedData: FALSE,
recordNotFound: FALSE, crcError: FALSE, track00: TRUE, index: FALSE,
busy: FALSE];
END.....
LOG
Time: September 26, 1980 3:30 PM, by DDavies, start coding.
Time: October 21, 1980 2:25 PM, add mapping of all pages for virt addresses
Time: October 30, 1980 5:17 PM, add mapping of IOCB addresses
Time: November 7, 1980 1:00 PM, add byte swapping for IDs read
Time: November 12, 1980 11:13 AM, by DDavies, add bulletproofing against polling out of order.
Time: December 1, 1980 1:35 PM, by DDavies, remove byte-swapping.
Time: December 1, 1980 10:36 PM, by Forrest, run through reformatter.
Time: January 22, 1981 11:10 AM, by Gobbel, fix bug that causes head to always die if function=formatTrack.
Time: March 23, 1981 7:06 PM, by Forrest, fix bug causing recordNotFound when recalibrate/recover with count=0.
(1792)\f8