*----------------------------------------------------------- Title[DiskSubrs.mc...February 12, 1983 2:10 PM...Taft]; * Disk subroutines shared by microcode for all disk formats. *----------------------------------------------------------- Set[XTask, IP[DSK]]; KnowRBase[DiskRegs]; TopLevel; :If[AltoMode]; ********** Alto version ********** *----------------------------------------------------------- * This label gets defined once here, and redefined in TriDisk.mc if loaded. * It is defined here because it gets bound too early if included * in ADiabloDisk.mc *----------------------------------------------------------- EndAltoLoop: KStatus← A0, Block, Branch[AltoLoop]; :EndIf; ********************************** *----------------------------------------------------------- Read1Muff: * Read one muffler * Enter: KTemp0 = muffler number * Exit: KTemp0 = T = muffler value (0 or 1, right-justified); TIOA[DiskMuff] *----------------------------------------------------------- Subroutine; TIOA[DiskMuff], Global; Output← KTemp0; Nop; * 1-cycle wait required by hardware KTemp0← T← Input, Return; *----------------------------------------------------------- Read20Muffs: * Read 20 consecutive mufflers * Enter: T = starting muffler number * Exit: KTemp0 = T = word full of muffler values, left-to-right * TIOA[DiskMuff] * Clobbers KTemp1 *----------------------------------------------------------- Subroutine; KTemp1← T, TIOA[DiskMuff]; KTemp0← T← 1C; * Flag bit shifted out when done Read20MLoop: KTemp1← (KTemp1)+1, Output← KTemp1; KTemp0← ((KTemp0) OR T) LSH 1; T← Input, Branch[Read20MLoop, ALU>=0]; * Tests unshifted ALU output KTemp0← T← (KTemp0) OR T, Return; *----------------------------------------------------------- DoMuffOutput: * Do output to muffler register, for clearing TWs, etc. * Enter: T = muffler command * Exit: TIOA[DiskMuff] *----------------------------------------------------------- Subroutine; TIOA[DiskMuff], Global; Output← T, Return; *----------------------------------------------------------- SetDriveAndSubSector: * Select new drive and set subsector counter * Enter: T = drive number * KTemp0 = subsector count -1 (the way the hardware wants it) * Exit: KSelect updated to new drive number * Clobbers T, KTemp0; invalidates Sector * Clears all TWs and sets BlockTilIndex. *----------------------------------------------------------- Subroutine; Sector← Link; TopLevel; KSelect← (KSelect) AND (Not[tagDriveNumber!]C); * Mask out old drive number KSelect← T← (KSelect) OR T; * Insert new drive number KTemp0← LSH[KTemp0, 6]; * Combine subsector count with drive T← (KTemp0) OR T, Call[SendDriveTag]; * Select the drive T← T OR (tagLoadSubSector), Call[SendDriveTag]; * Load subsector count * There is a potential race here. We must first clear all TWs, then set * BlockTilIndex, then clear SectorTW which might have come up in the meantime. * Fortunately, IndexTW does a DC reset of BlockTilIndex if it comes up * in the meantime. T← clearAllTWs, Call[DoMuffOutput]; TIOA[DiskControl]; * T← blockTilIndex T← blockTilIndex, Call[OutputGetsT]; T← clearSectorTW; Sector← (Sector)-(Link← Sector)-1, * Sector← -1 to invalidate it Branch[DoMuffOutput]; * Clear SectorTW and return *----------------------------------------------------------- SendDriveTag: * Select new drive * Enter: T = drive number (perhaps including subsector count) * Exit: T unchanged; TIOA[DiskTag]; * Clobbers KTemp0 *----------------------------------------------------------- Subroutine; KTemp0← Link; TopLevel; TIOA[DiskTag], Call[OutputGetsT]; * Send just drive number T← T OR (tagDrive), Call[OutputGetsT]; * Strobe the DriveTag T← T AND (tagBus); Link← KTemp0, Branch[OutputGetsT]; * Lower DriveTag and return *----------------------------------------------------------- SeekAndWaitForReady: * Issue seek command and wait until it is done * Enter: T = cylinder number * Exit: Seek completed * Clobbers T, KTemp0, KTemp1, KTemp2 * May leave a SectorTW or IndexTW dangling. *----------------------------------------------------------- Subroutine; KTemp2← Link; TopLevel; T← T OR (tagCylinder), Call[SendTag]; SeekWait: * Wait for seek to complete KTemp0← muffNotReady, Call[Read1Muff]; KTemp0, Branch[SeekDone, R even]; * Branch if ready KStatus← A0, Block, Call[UpdateSector]; * Not ready, await next sector Branch[SeekWait]; SeekDone: T← A0, Link← KTemp2, * Restore Link and return Branch[OutputGetsT]; * (Output← 0 to DiskMuff does nothing) *----------------------------------------------------------- SendTag: * Send tag command and wait for tag sequence to complete * Enter: T = tag command * Exit: TIOA[DiskMuff] * Clobbers T, KTemp0, KTemp1 * Clears SeekTagTW, and counts sectors while waiting. *----------------------------------------------------------- Subroutine; KTemp1← Link, Global; TopLevel; KTemp0← T, TIOA[DiskMuff]; * First, clear SeekTagTW T← clearSeekTagTW, Call[OutputGetsT]; T← KTemp0, TIOA[DiskTag], Call[OutputGetsT]; * Issue the tag command * Wait until a SeekTagTW occurs, and handle sector wakeups while waiting. SendTagWait: Call[UpdateSector]; KTemp0← muffSeekTagTW, Block, Call[Read1Muff]; T← clearSeekTagTW, KTemp0, Branch[.+2, R odd]; Branch[SendTagWait]; * Wakeup wasn't SeekTagTW, wait more * Got a SeekTagTW. Clear it and return. Know TIOA[DiskMuff]. Link← KTemp1, Branch[OutputGetsT]; *----------------------------------------------------------- WaitForSector: * Wait for desired sector to arrive * Enter: (Alto) KAddr = desired sector number * (Pilot) KTemp3 = desired sector number * Call: SCall[WaitForSector]; * Returns +1: failed to reach desired sector within 64 sector times * +2: succeeded: * TIOA[DiskControl] * Clobbers T, KTemp0, KTemp1, KTemp2 *----------------------------------------------------------- Subroutine; KTemp1← Link; TopLevel; KTemp2← -100C; SectorWaitLoop: T← ClearSeekTagTW, Call[DoMuffOutput]; * Clear spurious SeekTagTWs Call[UpdateSector]; * Returns with T = Sector :If[AltoMode]; ********** Alto version ********** PD← (KAddr) XOR T, TIOA[DiskControl]; :Else; ******** PrincOps version ******** PD← (KTemp3) XOR T, TIOA[DiskControl]; :EndIf; ********************************** PD← A0, Link← KTemp1, Branch[SectorFound, ALU=0]; Subroutine; KTemp2← (KTemp2)+1, Block, Branch[SectorWaitLoop, R<0]; Return; * Failure return SectorFound: Return[ALU=0]; * Normal +2 return *----------------------------------------------------------- UpdateSector: * Check for sector/index wakeups and update Sector register * Enter: Sector = current sector number (-1 => invalid) * MaxSectors = maximum number of sectors expected for this disk format * KSelect[4] = 1 iff sectors do not evenly divide the disk * Exit: Sector updated; T = Sector * TIOA[DiskMuff] * Clobbers KTemp0 * Clears any SectorTW or IndexTW that occurs, but leaves other wakeups alone. * Note that due to a design blunder, IndexTWs do not occur if the current drive * is deselected. When we see SectorTW, we compute Sector← (Sector+1) mod MaxSectors. * Thus once we have seen one IndexTW we will stay synchronized. * Additionally, due to another design botch, the index pulse does not cause a SectorTW * if the number of subsectors/sector does not evenly divide the number of * subsectors/revolution; so when the drive is deselected we miss the index pulse entirely! * Therefore we must let the successor of MaxSectors-1 be 1 rather than 0; we can * reset to 0 only when we actually see an index pulse. * Note on sector numbering: * For an n-sector format, the sector immediately before Index is numbered n-1 * and the sector immediately after Index is numbered 0, for the purpose * of issuing commands. However, since commands are executed in the sector * after they are issued, the sector before Index is n-2 and the sector after * Index is n-1, as physically recorded on the disk. * This means that if the sectors do not evenly divide the disk, the unusable * short sector immediately before Index is the one numbered n-2, not n-1 * as you might expect. *----------------------------------------------------------- Subroutine; TIOA[DiskMuff], Global; T← muffSectorTW; * Select SectorTW muffler PD← (Sector)+1, Output← T; * Carry← 1 iff Sector = -1 T← muffIndexTW; KTemp0← Input; * Bit 15 ← SectorTW muffler KTemp0← Output← T, * Select IndexTW muffler Branch[NoSectorTW, R even]; * Branch if SectorTW not present Sector← T← (Sector)+1, XorSavedCarry; * Increment sector unless it is -1 PD← (MaxSectors) XOR T; KTemp0← (KTemp0) OR (clearSectorTW), Branch[.+2, ALU#0]; * Branch if did not wrap around T← LDF[KSelect, 1, 13]; * Reset to 0 or 1, depending on whether or not * IndexTW causes a SectorTW Output← KTemp0, Branch[.+2]; * Clear SectorTW, leave muffIndexTW selected NoSectorTW: T← Sector; KTemp0← Input; * Bit 15 ← IndexTW muffler KTemp0← Or[clearSectorTW!, clearIndexTW!]C, Branch[.+2, R even]; * Branch if IndexTW not present * If we see IndexTW, zero Sector and clear BOTH SectorTW and IndexTW. T← A0, Output← KTemp0; Sector← T, Return; *----------------------------------------------------------- ReadECC: * Read ECC following a block that was read (not checked). * A disk block read has: data, 2 garbage words, 2 ECC words. * Control gets here having read the first garbage word and executed at * least one additional microinstruction since the last Input. * Enter: TIOA[DiskData] * Exit: T = first ECC word * KTemp0 = second ECC word * ALU = (KTemp0) OR T *----------------------------------------------------------- Subroutine; * ReadFifoTW drops at T0 of the 4th cycle after Input, so we can Block * no earlier than the 5th cycle after Input. T← A0; * 2nd cycle T← T-1, Branch[., ALU=0]; * 3rd & 4th cycles Block; * 5th cycle * When we are awakened, we know we can input 3 words. * Note: cannot issue back-to-back Inputs, because OutReg is not reloaded * from the Fifo quickly enough. PD← Input; * Garbage word Nop; T← Input; * First ECC PD← A0, Branch[CheckECCLoop]; * Read second ECC and return *----------------------------------------------------------- CheckECC: * Read ECC following a block that was checked * Control gets here having read neither of the garbage words and executed at * least 2 additional instructions since the last Input. * Enter: TIOA[DiskData] * Exit: T = first ECC word * KTemp0 = second ECC word * ALU = (KTemp0) OR T * Clobbers KTemp1 *----------------------------------------------------------- Subroutine; KTemp1← 3C, Block; * ReadFifoTW drops at T0 of the 2nd cycle after Input, so we can Block * no earlier than the 3rd cycle after Input. CheckECCLoop: KTemp0← Input, Branch[CheckECCEnd, ALU=0]; T← KTemp0; Nop; KTemp1← (KTemp1)-1, Block, Branch[CheckECCLoop]; CheckECCEnd: PD← (KTemp0) OR T, Return; *----------------------------------------------------------- ClearDisk: * Reset disk controller and reset all TWs. * Enter: TIOA unknown * Exit: TIOA[DiskMuff]; KSelect initialized * Clobbers T, KTemp0, KTemp1 *----------------------------------------------------------- Subroutine; KTemp1← Link; TopLevel; T← DiskControl; * Need full TIOA for init TIOA← T; * Initialization after power-on is somewhat hairy because IOReset does not * completely reset the controller; in particular, it does not reset the * control or tag registers. It DOES reset the drive select register, so no * drive is selected and the undefined state of the tag register is harmless. * However, we must be careful to reset the tag register sufficiently in * advance of issuing a DriveTag so that there won't be a race between changing * the Select and Tag lines to the drives. The drive select register is loaded * on the 1-to-0 transition of the DriveTag signal. T← clearEnableRun; * Reset controller twice Output← T, Call[OutputGetsT]; TIOA[DiskTag]; T← tagDrive, Call[OutputGetsT]; * Ensure no 1-to-0 transition while * tag bus is changing T← A0, Call[OutputGetsT]; * Reset drive select register and clear * all tag bus latches T← clearAll, Call[DoMuffOutput]; * Clear all TWs * Initialize KSelect to select drive 0. Link← KTemp1; Subroutine; KSelect← tagSelectDrive, Return;