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; 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. csb: LONG POINTER TO CSB = LOOPHOLE[DLionInputOutput.IOPage + 111B]; -- 49'X 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 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 result(8): IOPResult, -- status posted by IOP samePage(9:0..0): BOOLEAN, -- TRUE => use same memory page for 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 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 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 unused1(0:1..1): BOOLEAN _ FALSE, 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): BOOLEAN _ FALSE, 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): BOOLEAN _ FALSE, busy(0:15..15): BOOLEAN]; -- => chip busy, should always be FALSE 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 CARDINAL _ SIZE[IOCB]; operationBlockLength: PUBLIC CARDINAL _ SIZE[IOCB]; 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] DiskChangeClear: PUBLIC PROCEDURE [DeviceHandle] = BEGIN ready: BOOLEAN; IF ~state.status.diskChange THEN RETURN; state.status.diskChange _ FALSE; ready _ ~Immediate[escapeC, diskChangeClearSC, nop].notReady; IF ready THEN [] _ Immediate[recalibrateC, nopSC, recalibrate] END; 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; GetNextDevice: PUBLIC PROC [device: DeviceHandle] RETURNS [DeviceHandle] = { RETURN[IF device = nullDeviceHandle THEN diskDeviceHandle ELSE nullDeviceHandle]}; GetContext: PUBLIC PROC [DeviceHandle] RETURNS [Context] = {RETURN[state.user]}; 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; 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}; 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; Start: PUBLIC PROC = {RemainingHeads.Start[]}; 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 = [ -- 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; 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; 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; 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 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; 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.notReady _ pResult.notReady; status.writeProtect _ state.user.protect OR pResult.writeProtect; 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; 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; 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; 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 ö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. *********************************************************************** 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. *********************************************************************** unused1(6: 0..31): UNSPECIFIED]; *********************************************************************** I O C B (and IOP command blocks) *********************************************************************** 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. (if format, track count) all sectors (unused if format) ((bytes)/4) MOD 256 order of these guys is significant to Head code as well as IOP latched until DiskChangeClear[] AND ~notReady *********************************************************************** 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) *********************************************************************** *********************************************************************** G L O B A L V A R I A B L E S *********************************************************************** 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. 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. Assumes an "Initialize" operation has been performed so "state.status" holds valid state bits ASSUMES EXACTLY ONE FLOPPY DRIVE IS CONNECTED TO THE DANDELION This code will only handles a single controller/single device configuration. 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. 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. exported to HeadStartChain ************************** PRIVATE PROCEDURES ************************** [512w][single] and [64w][double] are picked out of the air 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 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. update disk addr and client buffer parameters if twoSided, countDone can must <= 1 since we break runs at tracks, countDone <= 1 status.na3 _ FALSE; (_ TRUE) SA800 since Mokelumne pilot thinks this is a hardware error bit status.na1 _ FALSE; status.index _ FALSE; ++ IOP never sends an index pulse back set error flag if there was an error or the run proceeds off the end of the disk. change this to use checksum when implemented in microcode *********************************************************************** Various B O R I N G C O N S T A N T S *********************************************************************** ʘJšœ™Jšœ-™-J˜Jšœ ™ JšœF™FJšœF™FJšœ ™ Jšœ*™*Jšœ#™#J˜šÏk ˜ Jšœœ˜*Jšœœ ˜ Jšœ œ˜Jšœœ ˜Jš œœœœœ˜8J˜ J˜—šœ˜š˜J˜G—Jšœ˜!Jšœ ˜Jšœœ ˜J˜JšœG™GJšœ™Jšœ4™4JšœG™GJšœ5™5JšœG™Gš œœœ œœ˜&Jšœœ Ïc˜9Jšœ#ž%˜HJšœ œ˜ Jšœœž˜=Jšœ#ž-˜P—Jšœ!™!J˜Jš œœœœœœ#ž˜MJ˜JšœG™GJšœ$™$JšœG™GJš œ œœœœœ˜)Jš œ œœœœ ˜BJ˜Jšœœ˜'J˜š œœœ œœ˜'Jšœ*ž˜?Jšœ*ž˜AJšœœž$˜NJšœ*ž˜Cšœ*ž,˜VJšœJ™JJšœ5™5—Jšœ*ž˜IJ˜—š œ œœ œœ˜+Jšœœœž)˜PJšœ œ˜'Jšœœž)˜PJšœ'ž˜7Jšœ'ž˜Cšœœž(˜OJšœ™—Jšœ'ž˜>šœœ ž#˜JJšœ™—Jšœ'ž˜:Jšœ'ž&˜MJšœ'ž˜=Jšœœœ œ˜4J˜—š œœœ œœ˜1šœœž˜DJšœ™—Jšœ4ž˜LJšœœ ˜1Jšœœž˜LJ˜—Jšœ>™>šœ œœ œ˜#J˜OJ˜.J˜—Jšœ œœ œ*˜NJ˜šœœœ œ˜/J˜/J˜—š œ œœ œœ˜-Jšœœ œœ˜AJšœœ œ"˜GJ˜—š œ œœ œœ˜,šœœ ž$˜QJšœ ™ Jšœ™—Jšœœœ˜*Jšœœ ž'˜TJšœœ œ˜AJšœœ ž$˜QJšœœœ˜*Jšœœ ž&˜SJšœœ ž˜KJšœœ ž˜CJšœœ ž(˜UJšœœ ž˜JJšœœ ž$˜QJšœœ ž˜DJšœœ ž ˜MJšœœœ˜*Jšœœ ž'˜TJ˜—JšœG™GJšœ™JšœL™LJšœ™JšœG™Gšœœ œž˜DJ˜LJ˜J˜—šœ œ˜˜Jšœ œ*œ ˜OJšœBœ˜S—Jšœ"œ ˜4Jšœ&œ˜+J˜—˜Jšœœ˜J˜J˜Jšœœ˜0J˜0J˜J˜Jšœœ#˜6J˜J˜J˜—˜Jšœ œ œœ˜@Jšœœ œ œ˜:Jšœœœœ˜?Jšœ œ œœ˜.J˜—Jšœœœ˜4Jšœ(œ˜4Jšœ(œž"˜XJ˜Jšœœž*˜RJšœœž˜FJšœœž/˜WJ˜Jš œœœœœ˜6Jš œœœœœ˜6J˜J˜JšœG™GJšœ™JšœG™GJ˜J˜šœœ˜šœ1ž˜HJšœ œ œ&˜D—Jšœ1ž!˜RJšœ1ž%˜VJšœœž!˜RJšœ1ž˜IJ˜—JšœF™FJšœI™IJšœJ™JJšœH™HJšœ ™ šÏnœœ œ˜2Jš˜Jšœœ˜Jšœœœ˜(Jšœœ˜ J˜=JšœA™AJšœJ™JJšœK™KJšœO™OJšœU™UJšœ,™,Jšœœ1˜>Jšœ˜J˜—JšœF™FJšœ™šŸœœœœ˜FJš˜šœ˜Jšœœœœ˜BJšœœœœ˜7J˜G—Jšœ˜J˜—Jšœ>™>šŸ œœœœ˜LJšœœœœ˜RJ˜—Jš Ÿ œœœœœ˜PJ˜JšœL™Lš Ÿ œœœ œœœ˜IJš˜˜Jšœ-œ˜2Jšœ œœ˜—J˜'J˜J˜;J˜(Jšœœ˜Jšœ˜J˜—šŸœœœ˜/Jš˜J˜Jšœœ˜ š˜J˜:šœ˜˜Jšœœœœ˜0—J˜Jš˜—Jš˜—Jšœ˜J˜J˜——JšœÜ™Ü˜šŸœœœ˜'Jšœ+œ˜=Jšœœœ ˜&J˜šœœ!˜GJšœœœ˜L—Jšœœ˜=Jšœœœ ˜Išœ(˜.Jš˜Jšœœœ˜HJ˜šœœ ˜0˜6J˜+—J˜8J˜Jšœ˜ —Jš˜—Jšœœž3˜NJšœ˜J˜—šŸœœœœ˜>Jšœœœ ˜&Jšœœœ˜>šœœ˜(Jšœœœ˜H—Jšœ(œ˜=š˜šœ4œ œ˜MJšœœ˜Jšœœœ˜?Jšœ˜——Jšœ˜J˜—šŸœœœ˜#Jšœ$œ œ˜:J˜—JšœN™NJšœL™LJšœ™š Ÿ œœœ*œœ˜TJš˜Jšœ œ˜#š œœ"œœ œ˜TJšœž%˜*Jšž œ˜&Jšž œ˜&Jšž œ˜&Jšž œ˜'—šœ!œ˜AJšœ4œ ˜H—š œœœœ˜IJšœœ˜—J˜˜Jšœœœœ ˜P—J˜&Jšœœ˜ Jšœ˜J˜—Jšœ™JšŸœœœ˜.J˜JšœH™HJ˜šŸ œ œ˜&Jšœœ˜Jšœœœœ˜+Jšœ œ˜J˜Jšœ!œœ˜/J˜Jšœœœ˜9J˜&šœ ˜Jšœœž ˜)Jšœž˜4˜6Jšœœ˜Jšœ œ.˜=˜J˜˜Jšœœœ œ˜AJšœœœ œ˜4—Jšœœ˜5—J˜)J˜"J˜)˜Jš œ œœœœ˜:Jšœ œœœ ˜C—Jšœž˜6Jšœž$˜AJšœž*˜GJšœ˜—˜Jšœœ˜Jšœ œ˜#š œœ#œœ œ˜JJšœ:™:Jšœž%˜*Jšž œ˜&Jšž œ˜&Jšž œ˜&Jšž œ˜'—˜Jšœ0ž˜EJ˜/J˜+˜˜Jšœœœœ ˜8———šœ œ˜Jšœ œœœ ˜M—˜Jš œ œœœœ˜:Jšœ œœœ ˜C—J˜J˜J˜Jš˜—Jšœ˜—Jšœ˜J˜—JšœL™LJšœ5™5šŸ œ œœ˜?Jšœœ6˜@šŸ œœœœ˜1Jšœœœœ˜H—Jšœœœ˜>J˜Jšœœ˜.šœœ˜Jšœ#œ˜(J˜/˜Jšœœ˜9J˜—šœ˜˜Jšœ œœœ˜6Jšœ œœ˜-Jšœ+˜-——Jšœœ˜—Jšœœ œ˜JJšœ˜J˜—JšœT™TJšœR™RJšœR™RJšœO™OJšœA™AšŸ œ œœ˜?Jšœœ#˜-J˜&Jšœ(ž˜FJšœœœœ˜8J˜Jšœ-™-Jšœœ˜Jšœ œ"˜5J˜šœ ˜˜š œ œœœ˜$Jšœ$™$Jšœœ˜1Jšœ6˜=——˜:Jš˜šœ˜Jš˜šœœ˜Jšœœœ œ&˜L—J˜-J˜-Jšœ˜—˜*Jšœ œ2˜A—J˜Jšœ-™-š œ œœœ˜$Jšœœ˜1Jšœ6˜=—Jšœ˜—Jšœ˜—Jšœ˜J˜Jšœ.ž˜>˜J˜Jšœœ˜1Jšœœœ˜I—J˜/—šœ$™$JšœH™HJ˜/Jšœ1œ˜I—Jšœ$™$šœL™Lšœ ˜˜Jš˜J˜.J˜+J˜+˜Jšœœ'œ˜Q—Jšœ˜—šœ˜Jš˜J˜+J˜+˜Jšœœ'œ˜Q—Jšœ˜—˜Jš˜J˜+˜Jšœœ'œ˜Q—Jšœ˜—˜Jš˜J˜3J˜*Jšœ˜—˜J˜*—Jšœ˜—JšœQ™QJšœœ˜6Jšœ"œœ ˜OJšœ œœ˜9J˜)Jšœ˜J˜šŸ œ œ˜J˜=Jšœ˜Jš˜J˜0J˜3J˜1J˜=J˜<šœ˜Jšœ1˜8—Jšœ˜J˜—šŸ œœœœ˜7Jšž œ˜šœœ˜JšœDœ˜IJšœœ˜—Jšœ˜J˜—šŸœœ˜Jš˜J˜Jšœœ˜Jšœœ œ˜JJšœ˜J˜—Jšœ9™9š Ÿœœœœ œ œ˜CJš˜šœ˜ Jš˜Jšœœœ ˜šœ ˜J˜B—Jš˜—Jš œœ œœœ˜JJšœ˜J˜—JšœG™GJšœ*™*JšœG™G˜š œ œœ œœ œ˜IJšœ œœ œ˜Jš œ œ œ œœ˜;——˜š œ œœ œœ œ˜IJšœ œœ œ˜Jšœœ ˜?Jšœœ>˜_Jšœœ#˜BJšœœ&˜FJšœœL˜lJšœœS˜pJ˜ J˜—…—Ox