{File name: TridentBootDLion.mc Author: J. Furst Dennis Grundler: 2-Sep-84 15:53:28, add copyright notice. Edit: E. Neely, August 23, 1982 3:58 PM - change CHdrDelRdSet to CHDelRdSet. Edit: J. Furst, January 13, 1982 6:03 PM - change to xfer 28 secs. Edit: Amy Fasnacht, December 21, 1981 4:03 PM: Delete StartAddress Edit: J. Furst, November 2, 1981 5:06 PM Created: October 27, 1981 9:34 AM } { Copyright (C) 1981, 1982 by Xerox Corporation. All rights reserved.} { Description: Trident EPROM boot microcode, Version 1.0 This microcode is located in EPROMs on the IOP board together with Phase0 microcode. It reads 16 consequtive disk sectors (Initial microcode) starting @ Cylinder 0, Head 1, Sector 0 from Trident device 0 or device 1. The boot device address is a function of ALT Boot # (7 = device 0, 8= device 1). The operations performed by this microcode are: Select device (0/1 - as function of ALT Boot #) Test for disk onLine Recalibrate Wait for disk notBusy Seek head 1 Wait for indxFnd Start data xfer: Noop Hdr (check for errors = overrun & ECCError) Noop Lbl (check for errors = overrun & ECCError) Read Dt (check for errors = overrun & ECCError) The data xfer routine transters 16 consequtive sectors (RPageCount initially = 28) starting @ disk address (0, 1, 0) into memory starting @ location 100 (hex). In case of any unusual condition the microcode sets non zero value into a U register (dbUStatus) and also notifies the IOP via memory location 1 (abs). This microcode will not run properly without first being properly initialized. The following U and K registers: bU0400, bU0C00, bU0C04, bU0C0A, bU2C04, bUF000, bUDtPtrLo (100) and bUMask (600) must be initialized during the boot operation before TridentBootDLion is executed. ---------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------- Start of Code ------------------------------------------------------------------------} {The microcode starts at location BootStart. Test disk onLine. If not onLine yet wait for condition to became true; if already onLine prepare to perform recalibrate operation. In c2 RCnt gets the BUS value for Recal & DvcCkRst operation ored with 0C00. RAdr gets the TAG value shifted right by 8 for TagSetRst subroutine.} StartBoot: Ybus ← ~KStatus xor 0, YDisp, SetTask[4] ,c1; RCnt ← bU0C0A, DISP4[OnLine, 7], pCall4 ,c2, at[0F,10]; DiskNotOnLine: GOTO[StartBoot] ,c3, at[0F,10,OnLine]; DiskOnLine: RAdr ← CCtlTag, CALL[TagSetRst] ,c3, at[7,10,OnLine]; {After return from TagSetRst subroutine wait for arm motion to be completed. Set WU: DskRdy into KCtl register. If arm is still in motion the microcode will stay dormant until disk is ready.} WaitDiskRdy: RAdr ← bUBitSignAdr ,c1, at[7,10,RetTagSetRst]; RAdr ← RAdr or CWURdy ,c2; KCtl ← RAdr LRot0 ,c3; {Prepare to seek to Head 1. RCnt gets the BUS value = 1, RAdr gets the TAG value shifted right by 8 for the subroutine.} HeadSeek: RCnt ← bU0C00 ,c1; RCnt ← RCnt or 1, pCall4 ,c2; RAdr ← CHdTag, CALL[TagSetRst] ,c3, at[0,10]; {Wait for index. First reset the indxFnd FF and then set WU: indxFnd into KCtl register. The microcode will stay dormant until index is found.} WaitForIndx: RAdr ← bUBitSignAdr, ClrKFlags ,c1, at[0,10,RetTagSetRst]; RAdr ← RAdr or CWUIndxFnd ,c2; KCtl ← RAdr LRot0 ,c3; {Start the data xfer routine. Transfer sector starts here with setting the BUS with Head select bit. The CNTLTAG is set in c3.} XferSecStart: KCmd ← bU0C04 ,c1; RAdr ← bUBitSignAdr ,c2; RCnt ← KCmd ← bU2C04 ,c3; {The operation performed on Hdr field will be a Noop. First load sequencer into Hdr Noop routine.} HdrNoop: RAdr ← RAdr or 82, pCall4 ,c1; RCnt ← CHDelRdSet, CALL[DelSetRd] ,c2, at[0,10]; {Set the word count into RCnt register (2 for Hdr op) and the call the loop. The sequencer generates always wordCount+1 dataReqs in order to exit the loop.} HdrSetWUDtRq: pCall4 ,c2, at[0,10,RetDelSetRd]; RCnt ← 2, CALL[NoopLp], ZeroBr ,c3, at[1,10]; {This is the return point from Noop loop. After return the microcode reset the read bit in KCmd. Set the xfer mask into RCnt and call the TestXferErr subroutine.} HdrRstRd: KCtl ← bUBitSignAdr, pCall4 ,c2, at[1,10,RetDelSetRd]; RCnt ← bUMask, CALL[TestXferErr] ,c3, at[1,10]; {The operation performed on Lbl wiil be a Lbl Noop. First load sequencer into Lbl Noop routine.} LblNoop: RAdr ← RAdr or 84, pCall4 ,c1, at[1,10,RetXferOK]; RCnt ← CDelRdSet, CALL[DelSetRd] ,c2, at[2,10]; {Set the word count into RCnt register (10 for Lbl op) and the call the loop. The sequencer generates always wordCount+1 dataReqs in order to exit the loop.} LblSetWUDtRq: pCall4 ,c2, at[2,10,RetDelSetRd]; RCnt ← 0A, CALL[NoopLp], ZeroBr ,c3, at[3,10]; {This are the return points from Noop loop. After return the microcode reset the read bit in KCmd. Set the xfer mask into RCnt and call the TestXferErr subroutine.} LblRstRd: KCtl ← bUBitSignAdr, pCall4 ,c2, at[3,10,RetDelSetRd]; RCnt ← bUMask, CALL[TestXferErr] ,c3, at[3,10]; {The operation performed on Dt will be a Dt Read. First load sequencer into Dt Read routine.} DtRead: RAdr ← RAdr or 8E, pCall4 ,c1, at[3,10,RetXferOK]; RCnt ← CDelRdSet, CALL[DelSetRd] ,c2, at[4,10]; {Set WU: dataReq for Dt operation and the word count into RCnt register (100) and go to DtRdLp to xfer data. The sequencer generates always wordCount+1 dataReqs in order to exit the loop. Restore initially the memory address into RAdr register.} DtSetWUDtReq: RAdr ← bUDtPtrLo, pCall4 ,c2, at[4,10,RetDelSetRd]; RCnt ← CDelRdRst + 0, PgCarryBr, CALL[DtRdLp] ,c3, at[5,10]; {Reset the Read bit. Set the Hdr mask into RCnt and callthe xfer XferTestErr subroutine.} DtRstRd: KCtl ← bUBitSignAdr, pCall4 ,c2, at[5,10,RetDelSetRd]; RCnt ← bUMask, CALL[TestXferErr] ,c3, at[5,10]; {Test for page count = 0 (page count is saved in RPageCount register during the entire Trident read operation). If yes go to EndProc; if no reset BUS and TAG lines.} ZrPgCntTest: RPageCount ← RPageCount - 1, NZeroBr ,c1, at[5,10,RetXferOK]; RAdr ← RAdr or CWUSecFnd, BRANCH[ZrPgCnt, NotZrPgCnt] ,c2; ZrPgCnt: KCmd ← bU0C00, GOTO[EndProc] ,c3, at[0,2,NotZrPgCnt]; NotZrPgCnt: KCtl ← RAdr LRot0, GOTO[XferSecStart] ,c3, at[1,2,ZrPgCnt]; {Deselect the disk and reset FirmwareEn bit in KCmd.} EndProcErr: dbUStatus ← RAdr, GOTO[EndProcC2] ,c1; EndProc: dbUStatus ← 0 ,c1; EndProcC2: KCtl ← bUF000 ,c2; KCmd ← bU0400, GOTO[StartBoot] ,c3; {--------------------------------------------------------------------------------------------------------------------------------------------------------------- SUBROUTINES ---------------------------------------------------------------------------------------------------------------------------------------------------------------} {This subroutine is used to transfer commands to the selected disk. Upon entry RCnt has the Bus value and RAdr the Tag constant shifted right by 8. Subroutine sets the Bus in c1 and the Tag in c3 of click1.} TagSetRst: KCmd ← RCnt LRot0 ,c1; RAdr ← RAdr LRot8 ,c2; KCmd ← (RCnt or RAdr) LRot0 ,c3; {In c1 RPageCount register gets the initial page count = 28. In c3 of click2 the tag is reset. Before return RCnt is loaded with CTstFirstIOCB constant for use in seek routine if entered from Recal routine.} RPageCount ← 1C ,c1; pRet4 ,c2; KCmd ← RCnt LRot0, RET[RetTagSetRst] ,c3; {This is the entry point of the Noop loop. It is used to clock over a particular field in order to sustian orientation. KStrobe is used to reset Data Requests.} NoopLp: KStrobe, BRANCH[NoopLpC2, EndNoopC2] ,c1; NoopLpC2: Noop ,c2, at[0,2,EndNoopC2]; RCnt ← RCnt-1, ZeroBr, GOTO[NoopLp] ,c3; {This is the exit from the Noop loop. It resets the LdSequencer and sets a delay constant in RCnt to delay the read bit reset before returns from delay subroutine.} EndNoopC2: RCnt ← CDelRdRst, GOTO[DtRdEndC3] ,c2, at[1,2,NoopLpC2]; {This is hte loop, which transferes the data field from the controller to memory. The buffer has to start on the page boundary for this loop to work properely.} DtRdLp: MAR ← [RHAdr, RAdr + 0], BRANCH[DtRdLpC2, DtRdEndC2], KStrobe ,c1; DtRdLpC2: MDR ← KIData ,c2, at[0,2,DtRdEndC2]; RAdr ← RAdr + 1, PgCarryBr, GOTO[DtRdLp] ,c3; {This is the end of the data xfer routine. The memory address is saved in bUDtPtrlo for next sector operation and then the DelSetRd subroutine is called to reset the read bit.} DtRdEndC2: bUDtPtrLo ← RAdr ,c2, at[1,2,DtRdLpC2]; DtRdEndC3: RAdr ← KCtl ← bUBitSignAdr, GOTO[DelSetRdC1] ,c3; {Set the sequencer into the proper routine.} DelSetRd: KCtl ← RAdr LRot0 ,c3; {This is the delay loop. It sets the disk read circuitry before Hdr noop, Lbl noop and Dt read operations. In c3 RAdr gets bUBitSignAdr.} DelSetRdC1: RCnt ← RCnt - 1, ZeroBr ,c1; RHAdr ← 0, BRANCH[DelSetRdLp, DelSetRdEnd] ,c2; DelSetRdLp: RAdr ← bUBitSignAdr, GOTO[DelSetRdC1] ,c3, at[0,2,DelSetRdEnd]; DelSetRdEnd: RCnt ← bU2C04, GOTO[SetRead] ,c3, at[1,2,DelSetRdLp]; {Set constant into RCnt to set read bit in c1 In c2 set the CNTLTAG and and BUS for all operations. In c3 CWUDtReq is ored into RAdr register for use in next click.} SetRead: RCnt ← RCnt or CRead ,c1; KCmd ← RCnt LRot0 ,c2; RAdr ← RAdr or CWUDtReq, pRet4 ,c3; {Set WU: dataReq for all operation. This will be needed in all loops} KCtl ← RAdr LRot0, RET[RetDelSetRd] ,c1; {This subroutine test for errors after each field is processed. In case of an xfer error RAdr gets 1 to address and store 1 into memory location 1.} TestXferErr: Ybus ← ~KTest and RCnt, ZeroBr ,c1; KCmd ← bU2C04, BRANCH[XferErr, XferOK], pRet4 ,c2; XferErr: RAdr ← 1, ClrKFlags, CANCELBR[EndProcErr] ,c3, at[0,10,XferOK]; XferOK: RAdr ← bUBitSignAdr, ClrKFlags, RET[RetXferOK] ,c3, at[1,10,XferErr];