.MCALL .MODULE .MODULE DU,VERSION=188,COMMENT=,AUDIT=NO ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only with the ; inclusion of the above copyright notice. This software, or ; any other copies thereof, may not be provided or otherwise ; made available to any other person except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. ;++ ; Facility: RT-11 MSCP Disk Class Handler ; ; Edit Who Date Description of modification ; ---- --- ---- --------------------------- ; 001 WLD 23-FEB-90 Replaced Primary Bootstrap ; with unconditionalized version ; that does not use location 60 ; 002 WLD 20-MAR-90 Fixed possible bug: MFPT loads ; only low byte of R0. Operations ; on R0 should by byte-oriented. ; 003 WLD 26-JUN-90 Changed software boot to assemble ; BDUAIP as -1 and to support ; booting on multiple partitions. ; 004 WLD 10-AUG-90 Fixed build problem in the SETOVR ; .PSECT. ; 005 WLD 26-JUL-91 Fix PSECT SPFUN to include ; SF.AWR and omit duplicate of SF.W32 ; 006 WLD 13-DEC-91 Fix GETSIZ subroutine to calculate ; volume size correctly for both ; 16 and 32 bit interfaces. ; 007 WLD 06-FEB-92 For 16 bit I/O interface: disallow ; access to block 65,535. The ; SET [NO]HBTEST option controls ; testing for the high block. ; 008 WLD 25-FEB-92 For 16 bit I/O interface: check ; LBN against disk partition sizes ; in PSTAB. ;-- .SBTTL Conditional Assembly Summary ;+ ;COND ; DU$BBR (0) Dynamic Bad Block Replacement ; 0 no dynamic replacement ; 1 dynamic replacement ; ; DU$N64 (0) 64 - unit support ; 0 8 units only ; 1 up to 64 units ; ; DU$ERL (0) Error logging ; 0 No error logging ; 1 RT11 Error logging (pre-V5.5 error logging) ; 2 MSCP Error logging ; (Also called RT11 Extended Error Logging) ; DU$ERL supercedes the standard error ; logging conditional, ERL$G ; ; DU$UNIts(8. or 16.) Default number of units: 8 iff DU$N64=0, ; 16 iff DU$N64=1 ; ; DU$PORts(1) Supported ports ; 1-4 allowable range ; ; DU$NAM (^RDU ) Handler name (for global region also) ; ; B$DNAM (^RDU ) Device name used by bootstrap code ; ; DU$CSR (172150) 0th CSR ; DU$CS1 (DU$CSR-04) 1st CSR ; DU$CS2 (DU$CSR-10) 2nd CSR ; DU$CS3 (DU$CSR-14) 3rd CSR ; ; DU$ALT (176150) Alternate CSR (Falcon) ; ; DU$VEC (154) 0th Vector ; DU$VC1 (DU$VEC-04) 1st Vector ; DU$VC2 (DU$VEC-10) 2nd Vector ; DU$VC3 (DU$VEC-14) 3rd Vector ; ; In the following three lines, "nn" is ; the RT-11 UNIT number in octal from ; 0 - 77 ; ; MMG$T std conditional ; TIM$IT std conditional (no code effects) ; ERL$G std conditional - but superceded by ; DU$ERL, hence no code effects except for ; implicit use of it with .DR macros ; ; UM$xxx any conditional of the form UM$xxx is ; a copy of a DU conditional used by the ; common source module, UM.MAC. ; UM$xxx always equals DU$xxx ; ; DU$Unn (nn) MSCP UNIT number in translation table ; DU$Onn (0) PORT number in translation table ; DU$Ann (0) PARTition number in translation table ; ; Initially, the RT-11 unit numbers translate 1 to 1 with the MSCP ; unit numbers. The port number defaults to 0. Thus RT-11 unit DU0: ; is unit 0 of port 0; RT-11 unit DU1: is unit 1 of port 0; etc. ; ; The values may be SET. ; ; The DU$Ax value is the Partition Number (UT.PART). (MU uses this ; byte for "Owner Job#/Flag"). ; ;- .SBTTL .MCALLs, Default Conditional Definitions ;these are the SYSMAC.SML macros we use .MCALL .ADDR, .ASSUME,.BR, .DRDEF .MCALL .INTEN, .MFPS, .MTPS, .PRINT .MCALL .SYNCH, .TRPSET,.WAIT .LIBRARY "SRC:SYSTEM.MLB" ;these are the SYSTEM.MLB macros we use .MCALL .CF2DF .CF3DF .FIXDF .SYCDF .HSRDF .SFDDF .UBVDF .MCALL .P1XDF .MEMDF .ERSDF .ELIDF ;invoke system.mlb macros .CF2DF ;system configuration word number two .CF3DF ;system configutation word number three .FIXDF ;monitor fixed offsets .SYCDF ;system communication area, aka "impure area" .SFDDF ;special function code definitions .HSRDF ;handler service (.DRPTR) routines .UBVDF ;UB handler's vector area .P1XDF ;Jump table relative to P1EXT .ERSDF ;16-bit (old) absolute-LBN .spfun status codes .ELIDF ;error logger record types .SBTTL Check Sysgen Parameters ;+ ; Assign default values to sysgen parameters and do some error checking ;- .IIF NDF DU$CSR DU$CSR = 172150 ;Default Standard CSR: Port 0 .IIF NDF DU$VEC DU$VEC = 154 ;Default Vector: Port 0 .IIF NDF DU$ALT DU$ALT = 176150 ;Alternate "standard" CSR: Port 0 .IIF NDF DU$CS1, DU$CS1=DU$CSR-04 ;Default CSR: Port 1 .IIF NDF DU$CS2, DU$CS2=DU$CSR-10 ;Default CSR: Port 2 .IIF NDF DU$CS3, DU$CS3=DU$CSR-14 ;Default CSR: Port 3 .IIF NDF DU$VC1, DU$VC1=DU$VEC-04 ;Default Vector: Port 1 .IIF NDF DU$VC2, DU$VC2=DU$VEC-10 ;Default Vector: Port 2 .IIF NDF DU$VC3, DU$VC3=DU$VEC-14 ;Default Vector: Port 3 .IIF NDF MMG$T, MMG$T == 0 .IIF NDF EIS$T, EIS$T == MMG$T .IIF EQ EIS$T, .MCALL SOB .IIF NDF DU$BBR, DU$BBR == 0 .IIF NDF DU$ERL, DU$ERL == 0 .IIF NDF ERL$G, ERL$G == 0 .IIF NDF DU$N64, DU$N64 == 0 .IIF NE DU$N64, DU$N64 == 1 ;number of units defaults to either 8. (DU$N64=0) or 16. (DU$N64=1) .IIF NDF DU$UNITS, DU$UNITS = <8.*DU$N64>+8. ;make sure the defined values are legal .IIF LT DU$UNI-08., .ERROR DU$UNI,;Warning - DU$UNI Less Than 8 .IIF GT DU$UNI-64., .ERROR DU$UNI,;Warning - DU$UNI Greater Than 64 .IIF EQ DU$N64, DU$UNITS = 8. ;Number of ports (may be defined externally, ie. during sysgen). .IIF NDF DU$PORTS, DU$PORTS = 1 ;Default number of ports to 1. .IIF LE DU$PORTS-1, DU$PORTS = 1 ;Never let the number be less than 1. ; If the number of ports equals one - multi-port booting is automatically ; disabled. .IIF NDF DU$BRD, DU$BRD = 0 ;Nonzero causes debug messages to ;be printed by the BBR algorithm $1=0 ;Loop counter .REPT DU$UNITS .IRP Y,\$1 .IIF NDF DU$U'Y DU$U'Y = $1 ;Default the unit number .IIF NDF DU$O'Y DU$O'Y = 0 ;Default the port number .ASSUME DU$O'Y LE DU$PORTS .ASSUME DU$O'Y GE 0 .IIF NDF DU$A'Y DU$A'Y = 0 ;default the partition number $1=$1+1 .ENDR .ENDR DU$PRI =: 5 ;priority of MSCP devices .IIF NDF DU$NAM,DU$NAM = <^rDU > UM$NAM = DU$NAM ;default value of arg. to .DRDEF .IIF DF DU$COD, UM$COD = DU$COD ;default value of arg. to .DRDEF .IIF DF DU$VEC UM$VEC = DU$VEC ;default value of arg. to .DRDEF .IIF DF DU$PN2, UM$PN2 = DU$PN2 ;default value of arg. to .DRDEF .IIF DF DUSTS, UMSTS = DUSTS ;default value of arg. to .DRDEF .IIF DF DUDSIZ, UMDSIZ = DUDSIZ ;default value of arg. to .DRDEF .IIF DF DU$N64, UM$N64 = DU$N64 ;default value of arg. to .DRDEF ;equate DU$ symbols with UM$ symbols UM$POR = DU$POR ;For common code UM$BBR = DU$BBR ;For common code UM$ERL = DU$ERL ;For common code UM$CSR = DU$CSR ; UM$CS1 = DU$CS1 ; " UM$CS2 = DU$CS2 ; " UM$CS3 = DU$CS3 ; " UM$VEC = DU$VEC ; " UM$VC1 = DU$VC1 ; " UM$VC2 = DU$VC2 ; " UM$VC3 = DU$VC3 ; " UM$UNI = DU$UNI ; " UM$N64 = DU$N64 ; " UM$NAM = DU$NAM ;name to use instead of .MODULE's UM UM$PRI = DU$PRI ;Device interrupt priority level ;Equate DU$ unit definitions with the UM$ unit definitions $1=0 ;Loop counter .REPT DU$UNITS .IRP Y,\$1 .IIF NDF UM$U'Y UM$U'Y = DU$U'Y .IIF NDF UM$O'Y UM$O'Y = DU$O'Y .IIF NDF UM$A'Y UM$A'Y = DU$A'Y $1=$1+1 .ENDR .ENDR UM$DU = 1 ;We're building DU .SBTTL MSCP Disk Handler Functional Description ;+ ; Handler Functional Overview ; ; This handler provides support for MSCP disk class devices interfaced ; via a UDA-50 or compatible port controler. It features: ; ; o Full functional use of MSCP as required by RT-11 (not ; a subset). ; o Support for any MSCP disk class device (variable size ; volumes etc. plus partitioning for disk units larger ; than 64K blocks. ; o Transparent special function support to facilitate ; unlimited access to all MSCP features. ; o Active seek (byte count 0) which actually seeks the ; unit with a Q$WCNT field equal to 1. ; o Multiple port support as a SYSGEN option. ; o RT-11 unit translation for port number, CSR, vector, ; MSCP unit and disk partition. ; o Plus standard RT-11 reads and writes (of course). ; o Multi-port booting as a SYSGEN option. ; o 64-unit support as a SYSGEN option ; o By-pass recovery. ; o Bad Block replacement. ; o MSCP Error Logging support, as a sysgen option ; ; SET options: ; ; SET options in this handler manipulate control and translation tables ; which are used to map any RT-11 unit to any MSCP port, unit and ; partition. For example: ; ; SET DU5: PORT=3,UNIT=211,PART=2 ; ; would cause all references on DU5 to go to MSCP port number 3, ; unit number 211 and partition number 2. ; ; ; Special Functions: ; ; Code: Function: ; 360 MSCP Bypass (Additional code for compatibility w/MU handler.) ; ; 366 32-bit Absolute Write I/O ; ; 367 32-bit Absolute Read I/O ; ; 371 MSCP Bypass ; ; 372 Reads/Writes the RT-11 to MSCP unit translation table. ; (Note that MU does not use 372 for the same operation, MU ; uses 352 for this operation.) ; ; 373 Returns the volume size as per standard RT11. (VARSZ$) ; ; 376 Absolute Write I/O ; ; 377 Absolute Read I/O ;- .SBTTL SPFUN Code 372 - Read/Write RT-11 to MSCP Unit Translation Table. ;+ ; SPFUN Code 372 ; ; Q$WCNT = + -> READ TABLE ; = - -> WRITE TABLE ; ; Special function code 372 reads/writes the RT-11 to MSCP translation ; table. If the word count supplied by the user is too small, the returned ; table is truncated to fit - but entry for number of RT-11 units is correct. ; received/sent information. If the requested word count is 1, then an ; eight unit table is read; if requested word count is -1, then an eight ; unit table is written. ; Offset Size ; .--------------------------------. ;Table ID | RAD50 "DU_" | 0 2 ; .--------------------------------. ;Unit count | Number of RT-11 units | 2 2 ; .--------------------------------. ; | MSCP Unit Number | 4 2 ; RT-11 Unit 0 |--------------------------------| ; | UDA Port | Partition | 6 2 ; |--------------------------------| ; | MSCP Unit Number | 10 2 ; RT-11 Unit 1 |--------------------------------| ; | UDA Port | Partition | 12 2 ; '--------------------------------' ; / / ; / etc. / ;- .SBTTL SPFUN Code 371 (and 360) - MSCP Bypass ;+ ; SPFUN Code 371 (and 360) - MSCP Bypass ; ; Q$BLK = 0 -> DO NOT recover ; = 1 -> recover ; ; Special function code 371 enables direct access to the MSCP port. The ; buffer address in the .SPFUN request must point to a 52-word area ; in the user's job. The first 26 words are an MSCP envelope (a 16-bit ; packet length, then a virtual circuit ID word) followed by 24 words for ; an MSCP end message. The next 26 words are an MSCP envelope followed by ; 24 words to hold an MSCP command packet. The main difference between no ; recovery (Q$BLK = 0) and recovery (Q$BLK = 1) is that eliminating ; recovery leaves out the possibility of decrementing the retry counter, ; calling the error logger and trying again. ; ; ; ----------------------------------------- ; 0 | Response Packet Length | ; ----------------------------------------- ; 1 | Virtual Circuit ID (From UDA) | ; ----------------------------------------- ; 2 | MSCP Response Buffer (24 Words) | ; ----------------------------------------- ; / / ; / / ; ----------------------------------------- ; 26 | Command Packet Length (48.) | ; ----------------------------------------- ; 27 | Virtual Circuit ID (From Host) | ; ----------------------------------------- ; 28 | MSCP Command (24 Words) | ; ----------------------------------------- ; / / ; / / ; ----------------------------------------- ; 51 | Last Word of MSCP Command Packet | ; ----------------------------------------- ;- .SBTTL MSCP Specific Protocol Definitions (Disk) ; Connection Id code of Disk Type of Device DISKID =: 0 ;+ ; Command Packet Offsets ; ; Generic Command Packet Offsets and Field Lengths: ;- P.LBN =: 34 ;4 Logical Block Number P.PART =: 36 ; RT-11 partition is high order LBN. ;+ ; REPLACE Command Packet Offsets and Field Lengths: ;- P.RBN =: 14 ;4 Replacement block number ;+ ; End Packet Offsets ; ; Generic End Packet Offsets and Field Lengths: ;- P.FBBK =: 34 ;4 First bad block ;+ ; GET UNIT STATUS End Packet Offsets and Field Lengths: ;- P.TRCK =: 44 ;2 Track size P.GRP =: 46 ;2 Group size P.CYL =: 50 ;2 Cylinder size P.RCTS =: 54 ;2 RCT table size P.RBNS =: 56 ;1 RBNs / track P.RCTC =: 57 ;1 RCT copies ;+ ; ONLINE and SET UNIT CHARACTERISTICS End Packet Offsets and Field Lengths: ;- P.UNSZ =: 44 ;4 Unit size P.VSER =: 50 ;4 Volume serial number ;+ ; Control Packet Opcodes ;- OP.RPL =: 24 ;REPLACE Command OP.SEX =: 7 ;Serious Exception end message (see note below) ;+ ; Note: End packet opcodes are formed by ORing the end packet flag to the ; command opcode. For example, OP.RD!OP.END = OP.RD endcode. If the ; endcode is OP.END alone then the command was unknown. If the endcode is ; OP.SEX!OP.END then the command ended with a serious exception. ;- ;+ ; Command Modifiers ; ; Generic Command Modifiers: ;- MD.EXP =: 100000 ;Express Request MD.ERR =: 10000 ;Force Error ;+ ; AVAILABLE Command Modifiers: ;- MD.SPD =: 1 ;Spin-down ;+ ; GET UNIT STATUS Command Modifiers; ;- MD.NXU =: 2000 ;Next Unit ;+ ; ONLINE Command Modifiers: ;- MD.RIP =: 1 ;Allow Self Destruction MD.IMP =: 2 ;Ignore Media Format Error ;+ ; ONLINE and SET UNIT CHARACTERISTICS Command Modifiers: ;- MD.SWP =: 4 ;Enable Set Write Protect ;+ ; REPLACE Command Modifiers: ;- MD.PRI =: 1 ;Primary Replacement Block ;+ ; End Message Flags ;- EF.BBR =: 200 ;Bad Block Reported EF.BBU =: 100 ;Bad Block Unreported ;+ ; Unit Flags ;- UF.576 =: 4 ;576 Byte Sectors UF.RMV =: 200 ;Removable Media UF.BBR =: 100000 ;Controller Initiated BBR ;+ ; Controller Flags ;- CF.576 =: 1 ;576 Byte Sectors ;+ ; Error Log Message Packet Offsets and Field Lengths ;- L.VSER =: 44 ;4 Volume serial number ;+ ; Error Log message Format Codes ;- FM.DSK =: 2 ;Disk transfer errors FM.SDI =: 3 ;SDI errors ;+ ; Disk Transfer Errors Error Log Message Offsets ;- L.LVL =: 42 ;1 Level L.RTRY =: 43 ;1 Retry L.HDCD =: 50 ;4 Header code ;+ ; SDI Errors Error Log message Offsets ;- L.HDCD =: 50 ;4 Header code L.SDI =: 54 ;12 SDI Information .SBTTL BBR Control Table Element Offsets ;+ ; BBR Control Table Element Offsets ;- BB.STS =: 0 ;Status (Unit Flags) BB.MAX =: 2 ;Max User Addressable LBN (2 Words) BB.RCT =: 6 ;RCT Size BB.TRK =: 10 ;LBNs / Track BB.RBN =: 12 ;RBNs / Track BB.COP =: 13 ;Copies of RCT BB.BUF =: 14 ;pointer to response buffer BB.RNG =: 16 ;pointer to UQSSP ring BB.POR =: 20 ;pointer to port control table .IF EQ DU$ERL-2 BB.COD =: 22 ;hold a status event code BB.CRF =: 24 ;hold a command reference number BB.SIZ =: 30 ;Size of table entry .IFF ;EQ DU$ERL-2 BB.SIZ =: 22 .ENDC ;EQ DU$ERL-2 .SBTTL Driver Definitions ;+ ; Miscellaneous Declarations ;- IN.TMO =: 200 ;INITFL flag to require controller timeout to be set IN.ONL =: 100000 ;INITFL flag to require unit to be brought online IOP.NR =: 177777 ;INTEOP flag to mean internal operation/no recovery .SBTTL MACRO Definitions ; Push argument onto the stack! .MACRO PUSH LIST .IRP XX, MOV XX,-(SP) .ENDR .ENDM PUSH ; Pop arguments from the stack! .MACRO POP LIST .IRP XX, MOV (SP)+,XX .ENDR .ENDM POP .SBTTL .DRDEF - .DRPTR, .DREST, .DRSPF Macro Invocations ;+ ; Define RT-11 handler codes, vectors and CSRs. ;- NUMRS = 2 ;The number of Unibus Mapping Registers DU needs ;NOTE: because we are using the common source, UM.MAC, all the .DRxxx ;macros must have NAME=UM, not NAME=DU, for their "NAME" argument. ;This means that .DRDEF does not know about any DUxxxx symbols. It works ;only with UMxxxx symbols. So any defined DUxxxx symbols that .DRDEF ;plays with must, prior to invoking .DRDEF, must have been copied to ;UMxxxx symbols. ;Then the UMxxxx symbols that may have been defined by .DRDEF must, just ;after invoking .DRDEF, be copied to their DUxxxx counter parts. .IF NE DU$N64 .DRDEF UM,50,,0,172150,154,UNIT64=YES,DMA=YES,PERMUMR=NUMRS,SERIAL=YES .IFF ;NE DU$N64 .DRDEF UM,50,,0,172150,154,UNIT64=NO,DMA=YES,PERMUMR=NUMRS,SERIAL=YES .ENDC ;NE DU$N64 DU$NAM = UM$NAM DU$COD = UM$COD DU$VEC = UM$VEC .IIF DF UM$PN2, DU$PN2 = UM$PN2 ;this symbol around only if UNIT64=YES DUSTS = UMSTS DUDSIZ = UMDSIZ B$DNAM =: DU$NAM ; Define a device name for the bootstrap code .DRPTR FETCH=ONCE,LOAD=ONCE,UNLOAD=UNLOAD,RELEASE=UNLOAD .DREST CLASS=DVC.DK,MOD=DVM.DM .DRSPF SF.SIZ,TYPE=O ;373 Return Volume Size .DRSPF SF.TAB,TYPE=O ;372 Read/Write Unit Translation Table .DRSPF ,TYPE=O ;360 MSCP Bypass - 371 (OBY) is the ;old DU only Bypass code .DRSPF +SFTAB .SBTTL PSECT Ordering .PSECT UMDVR ;Handler Beginning (Low Mem in XM) .PSECT UMLORD ;Low Mem ORDER DEPENDENT Impure Data LOWBAS:: .PSECT UMLDATA ;Low Mem Impure Data .IF EQ MMG$T .PSECT UMDATA ;Impure Data (Low Mem in SJ/FB) .PSECT UMY ;Low memory BBR algorithm UMYBAS:: .ENDC ;MMG$T .PSECT UMLCODE ;Handler Code (Low Mem in XM) .PSECT SETOVR ;SET Code Overlays (Block Aligned) .PSECT UMLST ;Root to Root Refs to Relocate UM.LST:: .IF NE MMG$T .PSECT UMNDLS ;List End Psect .PSECT SETOV1 ;Rest of SET/INSTALL Overlay Code .PSECT LDOVR ;Load/Fetch code overlay .PSECT UMRLST ;Ext Mem UMX Refs to Root to Relocate .PSECT UYRLST ;Ext Mem UMY Refs to Root to Relocate .ENDC ;NE MMG$T .PSECT UMBOOT ;DU Bootstrap Code .IF NE MMG$T .PSECT UMXDAT ;Ext Mem ORDER DEPENDENT Impure Data ; (Hi Mem in XM) (Block Aligned!!) UMXBAS:: ;Base of Ext Mem PSECTs UMXDBA:: .PSECT UMDATA ;Impure Data (Hi Mem in XM) DATABA:: .PSECT UMX ;Ext Mem Handler Code (Hi Mem in XM) UMXBA:: .IF NE DU$BBR .PSECT UMXPAD ;force UMY to start on a paragraph .ENDC ;NE DU$BBR ;boundry .PSECT UMY ;the other high memory psect - it is UMYBAS:: ;intended for Host Initiated BBR stuff .ENDC ;NE MMG$T .PSECT SPFUNS ;over flow from block zero spfun table .PSECT $LAST$ ;Null PSECT should be last in map!!! .INCLUDE "SRC:UM.MAC" ;Include the code common with MU .TITLE DU - MSCP Disk Class Handler .SBTTL ************************* .SBTTL * DU * .SBTTL ************************* ; Now set the AUDIT trail .AUDIT .UM,.UMGEN,.UMUNI ;Universal (T)MSCP Class Handler .AUDIT .DU ;MSCP Port Handler .SBTTL Data Allocation ;********************************************************************* .PSECT UMDATA ;********************************************************************* VOLSIZE:.WORD 0,0 ;Size of current volume. SEEKIT: .WORD 0 ;Word to fake SEEKS into .IF NE MMG$T SEKBUF: .WORD 0 ;Buffer offset SEKPAR: .WORD 0 ;PAR1 relocation bias .ENDC ;NE MMG$T BLKLEN = 400 ;length of one block in words .IF NE DU$BBR BBRON: .WORD 0 ;BBR in progress flag ;now we switch to the psect which holds all of the bbr stuff ;********************************************************************** .PSECT UMY ;********************************************************************** P1UMX:: .WORD 0 ;place to save the UMX PAR1 mapping P1LW2:: .WORD 0 ;save the low memory par1 for UMY code Y$P1EX: .WORD 0 ;this is UMY's pointer to P1EXT ;+ ; BBR Control Table ;- ACTBBR: .WORD 0 ;Pointer to current BBR entry ;Initially, all port entries are set .ASSUME BB.STS EQ 0 ;the unit flags word from the GUS ;end message .ASSUME BB.MAX EQ 2 ;number of LBN's in the unit - the ;mscp "unit size", low order bits ;followed by the high unit bits .ASSUME BB.RCT EQ 6 ;Replacememnt Control Table (RCT) size .ASSUME BB.TRK EQ 10 ;number of LBN's per track .ASSUME BB.RBN EQ 12 ;number of RBN's per track .ASSUME BB.COP EQ 13 ;number of copies of the RCT ;the next three words are stuffed for each invocation ;the BBR code. the BBR code must restore them to ;appropriate registers in order to call GETNEXT .ASSUME BB.BUF EQ 14 ;buffer of end message for current op. .ASSUME BB.RNG EQ 16 ;points to UQSSP ring for current port .ASSUME BB.POR EQ 20 ;points to port control table entry ;for this port .IF EQ DU$ERL-2 .ASSUME BB.COD EQ 22 ;hold a status/event code .ASSUME BB.CRF EQ 24 ;hold a command reference number .ASSUME BB.SIZ EQ 30 ;size of one BBRTAB entry .IFF ;EQ DU$ERL-2 .ASSUME BB.SIZ EQ 22 ;size of one BBRTAB entry .ENDC ;EQ DU$ERL-2 .ASSUME DU$PORTS LE 4 BBRTAB: .REPT DU$PORTS .BLKW BB.SIZ/2 .ENDR ;DU$PORTS .SBTTL BBR - Data Structures and Definitions ;+ ; Data Area - BBR ;- LBN: .BLKW BLKLEN ;Buffer for data from bad LBN SEC0: .BLKW BLKLEN ;Buffer for sector zero of repl control table VSN0 =: 0 ; Volume Serial Number, Low order VSN1 =: VSN0+2 ; Volume Serial Number VSN2 =: VSN1+2 ; Volume Serial Number VSN3 =: VSN2+2 ; Volume Serial Number, High order RCTFLG ==: VSN3+2 ; Flag word RF.WB=:1 ; Write-back caching in use flag RF.FE=:200 ; Forced error flag RF.BR=:20000 ; Bad RBN flag RF.P2=:40000 ; Phase 2 replacement in progres RF.P1=:100000 ; Phase 1 replacement in progres ; Reserved Word RLBNL=: RCTFLG+4 ; LBN being Replaced, Low order RLBNH=: RLBNL+2 ; LBN being Replaced, High order URBNL=: RLBNH+2 ; RBN being Used, Low order URBNH=: URBNL+2 ; RBN being Used, High order RRBNL=: URBNH+2 ; RBN being Replaced, Low order RRBNH=: RRBNL+2 ; RBN being Replaced, High order CID0 =: RRBNH+2 ; Cache ID, Low order CID1 =: CID0+2 ; Cache ID CID2 =: CID1+2 ; Cache ID CID3 =: CID2+2 ; Cache ID, High order CIN0 =: CID3+2 ; Cache Incarnation Number, Low order CIN1 =: CIN0+2 ; Cache Incarnation Number, High order RCTBF: .BLKW BLKLEN ;First buffer for replacement control table RCTB1: .BLKW BLKLEN ;Second buffer for replacement control table ;+ ; I/O Parameters for BBR ;- XFRSZ: .BLKW 1 ;Transfer size ; *** NOTE: DO NOT CHANGE order of next 2 lines (High Word MUST Come 1ST) *** .ASSUME LBNH+2 EQ LBNL LBNH: .BLKW 1 ;High block # of block to be transfered LBNL: .BLKW 1 ;Low block # of block to be transfered RPLBL: .BLKW 1 ;Replacement block number (Low order) RPLBH: .BLKW 1 ;Replacement block number (High order) DSKBF: .BLKW 1 ;Buffer to transfer to/from IOMOD: .BLKW 1 ;I/O Modifier save area UNIT: .BLKW 1 ;Unit number IOFUN: .BLKW 1 ;I/O Function save area BBRCOM: .BLKB P.CSIZ ;place to build a command for BBR I/O ;+ ; Replacement Control Table - Descriptor Flag Byte Definitions ;- DF.SEC =: 010000 ; 1 = secondary, 0 = primary DF.ALL =: 020000 ; 1 = allocated, 0 = unallocated DF.UNU =: 040000 ; 1 = unuseable, 0 = useable DF.NUL =: 100000 ; 1 = null entry, 0 = not null entry ;+ ; General Internal Storage ;- USTAT: .BLKW 1 ;Unit status US.OLP =: 1 ;Unit was doing an ONLINE process. IOST: .BLKW 2 ;I/O status block for disk I/O DPHYSL: .BLKW 1 ;Disk buffer physical address Low DPHYSH: .BLKW 1 ;Disk buffer physical address High BBRECU: .BLKW 1 ;BBR Recursion flag BLBNL: .BLKW 1 ;First bad block reported by controller BLBNH: .BLKW 1 ; ... PLBNL: .BLKW 1 ;LBN in first copy of the table PLBNH: .BLKW 1 ; ... where the primary RBN is stored PLBNO: .BLKW 1 ;Offset in $PLBNH:$PLBNL of primary RBN ; Do Not Change The Order Of The Following Locations !! ; Critically ordered so that it can be copied right from sector 1 of RCT RCTSZ: .BLKW 1 ;Replacement Control Table Size TRKSZ: .BLKW 1 ;Number of LBNs per track RBNPT: .BLKB 1 ;Number of RBNs per track RCTCP: .BLKB 1 ;Number of Replacement Control Table Copies MXLBN: .BLKW 2 ;Max user addressable LBN NRBNL: .BLKW 1 ;New RBN found by $SRCH, Low order NRBNH: .BLKW 1 ; ... and High order NLBNL: .BLKW 1 ;LBN in table where $SRCH found New RBN, Low NLBNH: .BLKW 1 ; ... and High order NLBNO: .BLKW 1 ;Offset in $NLBNH:$NLBNL of New RBN descriptor ORBNL: .BLKW 1 ;Old RBN found by $SRCH (matching LBN), Low ORBNH: .BLKW 1 ; ... and High order OLBNL: .BLKW 1 ;LBN in table where $SRCH found Old RBN, Low OLBNH: .BLKW 1 ; ... and High order OLBNO: .BLKW 1 ;Offset in $OLBNH:$OLBNL of Old RBN descriptor OLDSTA: .BLKW 1 ;Old Status for operation OLDFLG: .BLKW 1 ;Old end message flags for operation ORGCOD: .WORD 0 ;end critical ordering WRKSIZ ==: OLDFLG+2-XFRSZ ;Size in words of the Work area! INTFLG::.BLKW 1 ;Internal Flag holder BIT0 =: 1 ;Bit 0 BIT1 =: 2 ;Bit 1 A read in step 13 failed BIT2 =: 4 ;Bit 2 Data was written with FE=1 in step 12C BIT3 =: 10 ;Bit 3 A read or write in step 7 failed BIT4 =: 20 ;Bit 4 Doing a phase 2 recovery for an ONLINE BIT5 =: 40 ;Bit 5 The compare in step 8 failed BIT6 =: 100 ;Bit 6 The compare in step 12C succeeded BIT7 =: 200 ;Bit 7 Step 8 jumped to step 13 BIT8 =: 400 ;Bit 8 non-primary replacemnt .NLIST BEX MSG1: .ASCIZ /?DU-E-Replacement control table invalid/ MSG2: .ASCIZ /?DU-E-Software write protecting volume/ CRLF: .ASCII <15><12><0> .LIST BEX .EVEN .IF EQ DU$ERL-2 LENBBR = 66 ;length of BBR attempted error log packet LENDTE = 54 ;length of Disk Transfer Error Packet .WORD LENBBR ;size of BBR attempted error log packet .WORD DATGRM BBRPAK: .BLKB P.MSIZ ;Hold BBR attempted error log packet which ;is assembled by the Host BBR algorithm. .WORD LENDTE .WORD DATGRM DTEPAK: .BLKB P.MSIZ ;To hold the Disk transfer Error Packet. .BLKB ENVLEN PCBUF: .BLKW PC.ESZ ;copy of the port control table entry .ENDC ;EQ DU$ERL-2 RESPON: .BLKB P.MSIZ ;Hold end message of a BBR I/O so ;that the UQSSP ring descriptor's buffer is ;not tied up. .ENDC ;NE DU$BBR .IF NE MMG$T ;**************************************************************************** UMPSECT ;if we are in XM, this will bring us back to UMLCODE ;**************************************************************************** .IFF ;NE MMG$T .PSECT UMLCODE .ENDC ;NE MMG$T .SBTTL FNDISP - DU Device Specific Function Dispatch ;+ ; FNDISP ; ; This routine is called from the common code to handle some device ; specific SPFUNs: i.e., Absolute LBN Read/Write. ; ; If the SPFUN passed is not an Absolute Read/Write, the command is ; ignored because the SPFUN is unknown to the driver, and we immediately ; exit the driver as if the command was done. ; ; If the command is a 16-bit Absolute Read/Write (the ones that have ; a status word), then the following is done: ; ; (i) the status word that will be returned to caller, SPCIO, ; is initialized to 100000 ; (ii) Q$BUFF(R5) is saved in SPBUFA ; (iii) PAR1 bias is saved from Q$MEM into SPMEM ; ; If the command is a write (SF.W32 or SF.AWR) then the word count field ; in the queue element is negated to make IOXFER work with it correctly ; ; Input: ; FUNTYP = Function code from queue element ; R5 -> Current queue element ; CALL FNDISP ;- .ENABL LSB .IF NE MMG$T ;********************************************************************* UMXPSECT ;********************************************************************* .ENDC ;NE MMG$T FNDISP: CMPB FUNTYP,#SF.W32 ;is it 32-bit, LBN absolute write? BEQ 20$ ;branch if yes CMPB FUNTYP,#SF.R32 ;is it 32-bit, LBN absolute read? BEQ 30$ ;only in here if it is NOT a 32-bit LBN absolute write MOV #ES.SUC,(PC)+ ;Indicate special Read/Write SPCIO: .WORD 0 ;Store here as flag - ES.SUC=100000 MOV Q$BUFF(R5),(PC)+ SPBUFA: .WORD 0 ADD #2,Q$BUFF(R5) ;skip over status word ;If this is XM then, because we have changed Q$BUFF, we must make ;sure that the PAR1 bias/offset is still a normalized pair .IF NE MMG$T MOV Q$MEM(R5),(PC)+ ;Store PAR value for later SPMEM: .WORD 0 ;(PAR value of status return address) CMP Q$BUFF(R5),#20100 ;since we advanced the buffer pointer BLO 10$ ;by 2, we must make sure that it is SUB #100,Q$BUFF(R5) ;still normalized INC Q$PAR(R5) ;Update Q$PAR for the DMA write INC Q$MEM(R5) ;Update Q$MEM for memory (not needed ;here) 10$: .ENDC ;NE MMG$T CMPB FUNTYP,#SF.AWR ;Is it a valid special function? BLT NOFUN ;No, branch out because it is unknown BHI 30$ ;Is a read so don't negate Q$WCNT 20$: NEG Q$WCNT(R5) ;here if we are initiating SF.W32 or ;SF.AWR, so negate the word count to ;make IOXFER work with it correctly 30$: RETURN ;Common code will get it started. ;DU has been passed an NOFUN: TST (SP)+ ;invalid function: pop return address, CLR SPBUFA ;restore these flags CLR SPCIO ;note that in this case, the Q$BUFF field in the queue ;element is corrupted, but the monitor will not care CALLR DOFIN ;ignore the function, and go exit. .DSABL LSB .SBTTL UMHERR - Error and Standard Exits ;+ ; Exit to RT-11 with error flag set. ;- UMHERR: TST SPCIO ;Are we doing Absolute R/W? BEQ 10$ ;No, branch CALL SPCERR ;Go put error word before bye... 10$: GETCQE R0 ;R0 -> current queue element. BIS #HDERR$,@-(R0) ;Set the hard error bit. .BR UMEXIT ;Return to RT-11. ;+ ; Standard exit to RT-11 ;- .ENABL LSB UMEXIT: TST SPCIO ;Are we doing 16-bit Absolute R/W ? BEQ 20$ ;branch if not CALL SPCEXI ;Go put success word before bye... BR DOFIN 20$: CMPB FUNTYP,#SF.W32 ;is this the 32-bit absolute LBN BEQ 30$ ;write? branch if so. CMPB FUNTYP,#SF.R32 ;is this the 32-bit absolute LBN BNE DOFIN ;read? branch if not. ;here, we must return the actual word count of the transfer that ;occurred. we return it in the argument block that was passed to ;us via Q$BLKN. 30$: GETCQE R0 ;R0 -> current queue element. MOV P.BCNT(R5),-(SP) ;get the actual byte count, and ASR @SP ;convert it to the number of words .ASSUME Q$BLKN EQ 0 .IF EQ MMG$T MOV @R0,-(SP) ;get the pointer from Q$BLKN ADD #4,@SP ;skip the first two words MOV 2(SP),@(SP) ;move the word count into the 3rd word ADD #2,@SP ;advance to the fourth word. CLR @(SP) ;clear the fourth word. CMP (SP)+,(SP)+ ;remove pointers from the stack .IFF ;EQ MMG$T MOVB Q$JNUM(R0),R1 ;get the job number BIC #^cJOBMK,R1 ;mask out junk ASH #-3,R1 ;rotate job number down to low 3 bits MOV @R0,R0 ;get the pointer from Q$BLKN ADD #4,R0 ;skip the first two words, this is the MOV H$P1EX,R2 ;virtual address that will be CALL $CJVPT(R2) ;converted to a PAR1 bias/offset in ;R1 and R2 MOV (SP)+,R3 ; *C* R3 = the word count BCC 40$ GETCQE R0 ;R0 -> current queue element. BIS #HDERR$,@-(R0) ;Set the hard error bit. BR DOFIN 40$: .HP1EX R1 ;R1 = the PAR1 bias MOV R3,@R2 ;stuff the wordcount .HP1EN ADD #2,R2 ;advance to the fourth word CMP R2,#20100 ;must we adjust the mapping BLO 50$ ;branch if not SUB #100,R2 ;decrease offset INC R1 ;advance the bias 50$: .HP1EX R1 CLR @R2 ;clear the fourth word .HP1EN .ENDC ;EQ MMG$T DOFIN: .IF EQ UM$ERL-2 CLR QCHECK ;do not need the CRF of this CLR QCHECK+2 ;operation anymore. .ENDC ;EQ UM$ERL-2 RET2LO UMFIN .BR .IF NE MMG$T ;*********************************************************************** UMPSECT ;let's go back to UMLCODE for the .DRFIN ;*********************************************************************** .ENDC ;NE MMG$T UMFIN: .DRFIN UM ;Macro, macro what do you do? .DSABL LSB .SBTTL SPCERR - return 16-bit LBN SPFUN error status to caller ;+ ; SPCERR - A word with information concerning the termination of an ; I/O operation is added to the user buffer. ; ; Input: R1 -> status codes ; R2 -> controller flags ; ; There are five possible values for the status returned by the ; 16-bit LBN .SPFUNS: ; ; (i) Success - this case is taken care of by SPCEXI, below. ; <^o100000> ; ; (ii) Forced Error - returned if the block had been written ; with forced error, meaning that this is a bad block ; which has been replaced, but the BBR algorithm could ; not recover the data. <^o140000> ; ; (iii) Undetermined - if hard error has occurred on a block, ; and the DU device/controller requires Host Assisted ; BBR but the DU driver is not SYSGEN'ed for Host Assisted ; BBR (DU$BBR), this code is returned meaning that a ; block returned hard error and we do not even have a ; mechanism for BBR. In addition, this flag is used to ; indicate that the unit has no RCT at all. In summary, ; this code is for blocks that returned error and for ; which there no Bad Block Replacement mechanism ; exists. <^o100100> ; ; (iiia) The combination of (ii) and (iii) can be returned. ; (^o140100). This means A forced error was returned ; and there is no mechanism for replacement. For example, ; a block on an RX50 with the Forced Error flag on. ; ; (iv) Hard Error - non of the above. <^o177400> ;- .ENABL LSB ;********************************************************************* UMXPSECT ;********************************************************************* SPCERR: MOV (SP)+,(PC)+ ;save return address in a temporary 10$: .WORD 0 ;because the stack must be clean for ;the DO macro. MOV R1,(PC)+ 20$: .WORD 0 ;Do A Get Unit Status command because DO IGETUS,THEN,30$ ;the end message will tell us if the ;device uses Host Assisted BBR and, if 30$: MOV 10$,-(SP) MOV 20$,-(SP) ;not, whether or not it does BBR at BIC #^cST.MSK,@SP ;all (= RCT size not zero). CMP #ST.DAT,(SP)+ ;Was it a forced error ? BEQ 40$ ;Branch if it was a forced error. .IF EQ DU$BBR .ASSUME UF.BBR EQ 100000 TST P.UNFL(R5) ;Controller BBR? BMI 60$ ;branch if yes, and return hard error. BR 50$ .IFF ;EQ DU$BBR TST P.RCTS(R5) ;Does it have an RCT? BEQ 50$ ;branch if it does NOT have an RCT BR 60$ ;return hard error if it does .ENDC ;EQ DU$BBR 40$: BIS #ES.FRC,SPCIO ;set the forced error flag .IF EQ DU$BBR .ASSUME UF.BBR EQ 100000 TST P.UNFL(R5) ;controller BBR? BPL 50$ ;branch if not .ENDC ;EQ DU$BBR 45$: TST P.RCTS(R5) ;does it have an RCT? BNE 70$ 50$: BIS #ES.UDM,SPCIO ;return undetermined status BR 70$ 60$: BIS #ES.ERR,SPCIO ;set the hard error code 70$: CALL GETNEXT .BR SPCEXI .SBTTL Return 16-bit SPFUN status word to caller ;+ ; SPCEXI - A word with information concerning the termination of an ; I/O operation is added to the user buffer. ; ; Input: R1 -> status codes ; R2 -> controller flags ;- .ENABL LSB SPCEXI: MOV SPBUFA,R2 ;R2 -> user Q$BUFF .IF NE MMG$T MOV R4,-(SP) MOV H$CQE,R4 ;R4 -> current queue element MOV R2,Q$BUFF(R4) ;Store buff in Q-element MOV SPMEM,Q$MEM(R4) ;Restore mapping MOV SPCIO,-(SP) ;Store word to put CALL @H$PTWR ;Put status in user buffer MOV (SP)+,R4 .IFF ;NE MMG$T MOV SPCIO,(R2) ;Store in FB user buffer .ENDC ;NE MMG$T CLR SPCIO ;Reset flag RETURN ;Return to caller 10$: .WORD ;can save return address here .DSABL LSB .SBTTL IOXFER - Subroutine to Initialize for I/O Transfers ;+ ; This subroutine sets up the MSCP command buffer for I/O transfers. ; ; Input: R5 -> Queue Element ; R4 -> Command Packet (PAR1 Offset in XM) ;- .ENABL LSB IOXFER: MSCP OP.RD,USR.IO ;Create an MSCP read. .ASSUME Q$BLKN EQ 0 MOV @R5,R1 ;Save logical block number in R1; ;for an error check TST Q$WCNT(R5) ;Is this a SEEK ? BNE 10$ ;No, branch ;this is a SEEK .IF EQ MMG$T MOV #SEEKIT,Q$BUFF(R5) ;Read into SEEKIT instead $REL .-4 SEEKIT UMX ;$REL knows if we are in XM or not .IFF ;EQ MMG$T MOV SEKBUF,Q$BUFF(R5) ;So stuff in displacement MOV SEKPAR,Q$PAR(R5) ; and the PAR1 reloc bias .ENDC ;EQ MMG$T INC Q$WCNT(R5) ;Say read one word - so as to ;avoid a RQDX controller bug 10$: .IF EQ MMG$T MOV Q$BUFF(R5),P.BUFF(R4) ;Get the user's buffer address. .IFF ;EQ MMG$T ADD #Q$BUFF,R5 ;R5 -> Q.BUFF. CALL @H$MPPT ;Convert virtual to physical. MOV (SP)+,P.BUFF(R4) ;Get the low order 16 bits. MOV (SP)+,R0 ;Get the high order bits <9:4> ASH #-4,R0 ;Shift them into place. MOVB R0,P.BUFF+2(R4) ;Store them into MSCP buffer. MOV H$CQE,R5 ;R5 -> current queue element .ENDC ;EQ MMG$T TST SPCIO ;Are we doing a 16-bit absolute LBN BEQ 20$ ;.SPFUN? Branch if not SUB #2,Q$BUFF(R5) ;Undo affect of FNDISP, which was to .IF NE MMG$T ;advance Q$BUFF past the status word. CMP Q$BUFF(R5),#20000 ;normalize the PAR1 bias/offset pair BHIS 20$ ADD #100,Q$BUFF(R5) DEC Q$MEM(R5) .ENDC ;NE MMG$T 20$: MOV Q$WCNT(R5),R0 ;Get the word count. BPL 30$ ;Plus means it was a read. NEG R0 ;Make the word count positive. INCB P.OPCD(R4) ;Convert opcode to a write which .ASSUME OP.WR EQ OP.RD+1 30$: MOV R0,-(SP) ADD #BLKLEN-1.,R0 ;Adjust for partial read/write CLRB R0 ;Clear low byte SWAB R0 ;now R0 = number of blocks ;the following ADD yields the highest block number of the ;that this I/O operation reaches ADD R0,R1 ;Are we exceeding the partition ? ;if the resultant block number requires more than 16 bits, ;then the add caused an (unsigned integer) overflow which ;must set the C-bit BCC 40$ ;not exceeding the partition ;Overflow: need to adjust the wordcount - R1 contains excess. TST R1 ;Is it exactly zero ? BEQ 40$ ;Yes, no adjust necessary SUB R1,R0 ;R0 = Adjusted blocks SWAB R0 ;Make R0 into a word count. TST (SP)+ ;Pop R0 - not needed. BIS #HDERR$,@-(R5) ;Set hard error indicator in CSW BR 50$ ;Skip Next!! 40$: MOV (SP)+,R0 ;Restore old word count 50$: ASL R0 ;Convert to a byte count. MOV R0,P.BCNT(R4) ;Store in the MSCP buffer. CMPB FUNTYP,#SF.W32 ;32 bit absolute write? BEQ 60$ CMPB FUNTYP,#SF.R32 BNE 80$ 60$: .IF EQ MMG$T .ASSUME Q$BLKN EQ 0 MOV @R5,-(SP) ;Q$BLKN points to a double word which MOV @(SP),P.LBN(R4) ;contains the absolute LBN to ADD #2,(SP) ;read/write. MOV @(SP),P.LBN+2(R4) TST (SP)+ BR 90$ .IFF ;EQ MMG$T ;need to use a call to AT.SYS here if we are in XM PUSH ;protect registers .ASSUME Q$BLKN EQ 0 MOV @R5,R0 ;Q$BLKN -> the double word MOVB Q$JNUM(R5),R1 ;R1 := job number, BIC #^cJOBMK,R1 ;so isolate the job number bits ASH #-3,R1 ;rotate them down to correct position MOV H$P1EX,R2 CALL $CJVPT(R2) ;call the AT handler BCS 70$ ;C=1 means failure, so cannot do the ;I/O operation .HP1EX R1 ;use the PAR1 bias and offset returned MOV @R2,R1 ;by AT to obtain the 32-bit LBN from MOV 2(R2),R2 ;the job doing a bypass .HP1EN MOV R1,P.LBN(R4) ;put the LBN number in the packet. MOV R2,P.LBN+2(R4) CLC 70$: POP ;*C* BCC 90$ ;branch if all set to do I/O .ENDC ;EQ MMG$T 75$: GETCQE R5 BIS #HDERR$,@-(R5) ;Set hard error indicator in CSW TST (SP)+ ;remove return address CALLR DOFIN 80$: TST $HBT ;Test for high block? BNE 85$ ;BR if no: don't check LBN. CMP @R5,#177777 ;Is block 65,535 being accessed? BEQ 75$ ;BR if yes: error. .ASSUME Q$BLKN EQ 0 85$: MOV @R5,P.LBN(R4) ;get the low 16-bits of the LBN CLR P.LBN+2(R4) MOV .UTTAB,R5 ;Get ptr(UTTAB) for current RT-11 disk BISB UT.PARt(R5),P.LBN+2(R4) ;device unit & partition # as LBN(H). MOV .PSTAB,R1 ;Get ptr(PSTAB). TST @R1 ;Is MSCP unit Online? BEQ 95$ ;BR if not: DU will attempt to put the ;unit Online after I/O fails. CMP @R1,P.LBN(R4) ;Is there a block for the given LBN? BHIS 95$ ;BR if yes. TST P.LBN+2(R4) ;Is this partition 0? BNE 75$ ;No: cannot be floppy. TST @R1 ;Could MSCP unit be a floppy? BMI 75$ ;BR if not: LBN too big. BR 95$ ;BR if maybe: DU will attempt to put the ;unit Online after I/O fails. 90$: MOV .UTTAB,R5 ;R5 -> UTTAB entry. CLR R1 BISB UT.PART(R5),R1 ADD R1,P.LBN+2(R4) ;Get the hi order logical block no. 95$: RETURN ;Return to the caller. .DSABL LSB .SBTTL GETSIZ - Routine to Return the Volume Size (SPFUN 373) ;+ ; GETSIZ assumes that it itself is the post-interrupt activity of ; an MSCP ONLINE command - if the online command failed, hard error ; is returned ; ; If the volume is greater than 65K, then the volume size is the ; partition size. All partitions possibly except the last one are ; 65K - 1 blocks in length. ; ; For v5.5, the volume size is the number of blocks from the ; beginning of the partition on the MSCP unit defined in the RT-11 Unit ; Translation Table. ; For example, on port=0, if DU3 is SET to be UNIT=2 PART=3, the volume ; size returned for DU3 is the number of blocks in the volume mounted on ; MSCP unit 2 minus 3 partitions (3 * 65K blocks). ; For the RT-11 unit that is set UNIT=2 PART=0, the entire size of the ; volume mounted on MSCP unit 2 is returned. ;- .ENABL LSB GETSIZ:: PUSH ;protect registers MOV P.STS(R5),R4 ;get the status event code from the BIC #^c,R4 ;end message of the ONLINE. If the BEQ 10$ ;ONLINE failed we cannot obtain the POP ;unit's volume size. CALL GETNEXT ;Exit with hard error. CALLR UMHERR 10$: MOV P.UNSZ(R5),VOLSIZE ;Get the volume size, low order MOV P.UNSZ+2(R5),VOLSIZE+2 ;Get the volume size, high order .IF EQ DU$ERL-2 MOV P.VSER(R5),PC.VOL(R3) ;get the volume serial number MOV P.VSER+2(R5),PC.VOL+2(R3) .ENDC ;EQ DU$ERL-2 GETCQE R4 MOV .UTTAB,R5 .ASSUME Q$BLKN EQ 0 CMP @R4,#SF.S32 ;Which GETSIZ? BEQ 30$ ;BR for 32 bit (V5.5) GETSIZ. MOV #1,R0 ;16 BIT (PRE V5.5) GETSIZ. CLR -(SP) ;get starting LBN of partition. BISB UT.PART(R5),@SP ;(high order word only) CMP @SP,VOLSIZ+2 ;Is partition beyond end of volume? BHI 51$ ;BR if yes: soft error. BEQ 20$ ;BR if last partition (maybe partial). MOV #177777,@SP ;Otherwise, full 65 KBLKS. BR 40$ 20$: MOV VOLSIZ,@SP ;Partial partition: actually, VOLSIZ BR 40$ ;contains LBN of last adressable blk ;plus 1. 30$: MOV Q$WCNT(R4),R0 ;32 bit (v5.5) GETSIZ. CMP R0,#4 ;Ready to return 4 words? BNE 55$ ;BR if not: soft error. CLR -(SP) ;put the Logical Block Number (LBN) BISB UT.PART(R5),@SP ;of the first LBN in the partition CLR -(SP) ;onto the stack MOV VOLSIZE+2,-(SP) ;put the MSCP unit size onto the stack MOV VOLSIZE,-(SP) SUB 6(SP),2(SP) ;Calculate # BLKS remaining. BLO 50$ ;BR if beyond end of volume. 40$: .IF NE MMG$T 45$: JSR PC,@H$PTWR ;Return the size to the caller. .IFF ;NE MMG$T MOV Q$BUFF(R4),R1 45$: MOV (SP)+,(R1)+ .ENDC ;NE MMG$T SOB R0,45$ BR 60$ 50$: TST (SP)+ TST (SP)+ TST (SP)+ 51$: TST (SP)+ 55$: BIS #EOF$,@Q$CSW(R4) ;Return EOF (soft) error. 60$: POP CALL GETNEXT CALLR UMEXIT ;Take the normal RT-11 exit. .DSABL LSB .SBTTL .IF NE DU$BBR .SBTTL UMYJMP - *BBR* jump to BBR implementation .Psect ;+ ; UMYJMP - this where we goto when we want to transfer control from the ; UMX psect to the UMY psect. ; ; Input: if the caller wants to go to routine DESTIN in UMY, the caller ; does this ; ; MOV #DESTIN,-(SP) ; $REL .-2 DESTIN UMY ; JMP UMYJMP ; ; UMYJMP uses P1EXT to transfer control to the UMY routine GO2UMY. It ; is neccessary to us P1EXT because both UMY and UMX are in PAR1 memory ; regions. .enabl lsb UMYJMP:: .IF NE MMG$T MOV P1LOW,-(SP) ;PAR1 bias of low memory code MOV @#KISAR1,-(SP) ;save the current PAR1 bias .HP1EX P1UMY ;map to UMY JMP @#GO2UMY $REL .-2 GO2UMY UMY .HP1EN .IFF ;NE MMG$T JMP @(SP)+ .ENDC ;NE MMG$T .dsabl lsb .SBTTL GO2UMX - transfer control from UMY to UMX ; GO2UMX - this where we arrive when the UMY psect transfers control back to ; the UMX psect. it is intended that UMY is entered only from UMX and ; UMY returns back to only UMX. ; ; GO2UMX cleans up after the a P1EXT invocation GO2UMX: MOV 2(SP),SP ;restore stack from P1EXT CMP (SP)+,(SP)+ ;pop the values return by P1EXT MOV (SP)+,R0 ;restore R0 MOV (SP)+,P1UMY ;store the UMX mapping for return RETURN ;this Return transfers control to ;to the desired destination, to the ;location DESTIN (see JMPUMY, above) .SBTTL WRPUMX - *BBR* Do an IGETUS for the UMY psect ;+ ; WRPUMX - the purpose of this routine is to perform a Get Unit Status for ; the BBR algorithm which looks in the end message to see if the ; unit is currently write protected. Since the BBR algorithm is ; in the UMY psect (the other PAR1 space), it must come down to ; WRPUMX to initiate the I/O. ; ; In other words, the DO macro does not work from within the UMY ; psect, so at places in the UMY psect that want to do ; ; DO IGETUS,THEN,CKWRPT ; ; we must instead transfer control to here; the DO macro is invoked ; here such that when the Command is completed, we will ; (thanks to the magic of the DO macro) return right back ; to here an transfer control back to CKWRPT which looks in the ; end message for the write protect flags. ;- .ENABL LSB WRPUMX: DO IGETUS,THEN,10$ 10$: MOV #CKWRTP,-(SP) $REL .-2 CKWRTP UMY JMP UMYJMP .DSABL LSB .SBTTL DOSINK *BBR* - come to UMX psect to do a set S.U.C. command ;+ ;DOSINK - the bbr routine TROUBLE calls this routine here to software ; write protect a disk whose RCT is full or whose geometry is ; is corrupt. TROUBLE is in the UMY psect, so it cannot use ; the DO and the MSCP macro's. ; ;- .ENABL LSB DOSINK: MSCP OP.SUC,BBR.IO ;set up a Set Unit Characteristics MOV R4,R2 ;Command. SETSWP will put flags in DO SETSWP,THEN,10$ ;the command message that cause the ;SUC command to SOFTWARE WRITE 10$: CALL GETNEXT ;PROTECT the unit MOV #SINK,-(SP) ;now that the SOFTWARE WRITE PROTECT $REL .-2 SINK UMY ;is accomplished, go back to the JMP UMYJMP ;SINK routine, which performs .SYNCH .SBTTL SETSWP - set software write protection flag in a SUC command ;+ ; SETSWP - set software write protection flag in a Set Unit Characteristics ; command message. This is a subroutine of DOSINK. ;- .ENABL LSB SETSWP: MOV R2,R4 BIS #MD.SWP,P.MOD(R4) ;Enable soft write protect BIS #UF.WPS,P.UNFL(R4) ;Say soft write protect MOV #IOP.NR,INTEOP RETURN ;Return .DSABL LSB .SBTTL G2HION - *BBR* intermediate between low mem and BBR psect ;+ ; G2HION - this is a stepping stone between low memory and the routine ; HIONE which is in the UMY psect. It is part of the .SYNCH ; sequence. The routine TROUBLE, which is in the UMY psect, ; switches to a low memory psect in order to do the .SYNCH; ; then, from low memory, HCALL uses P1EXT to call G2HION, which ; in turn uses UMYJMP to go to HIONE. ;- .ENABL LSB G2HION: MOV #HIONE,-(SP) $REL .-2 HIONE UMY JMP UMYJMP .DSABL LSB .SBTTL GETUS - *BBR* Step 0* Save Information Necessary for BBR Support ;+ ; On entry - we are called from a succesful online command! We must ; save the controller flags and whatever is necessary before getting ; the unit status. ;- ;**************************************************************************** ; STEP 0 ;**************************************************************************** .ENABL LSB GETUS: MOV #ACTBBR,R1 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV @R1,R1 ;R1 (UMY) -> Active BBR MOV BB.BUF(R1),R5 ;R5 -> end message (ONLINE) MOV BB.RNG(R1),R4 MOV BB.POR(R1),R3 .HP1EN PUSH MOV P.UNFL(R5),R2 ;store the unit flags field MOV P.UNSZ(R5),R3 ;store the max LBN low MOV P.UNSZ+2(R5),R4 ;store the max LBN high .HP1EX P1UMY .ASSUME BB.STS EQ 0 MOV R2,(R1)+ ;Does it support BBR ? .ASSUME BB.MAX EQ 2 MOV R3,(R1)+ MOV R4,(R1)+ .HP1EN POP CALL GETNEXT ;because TRYONL is an internal ;operation with no recovery DO IGETUS,THEN,SAVUS SAVUS: MOV #ACTBBR,R1 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV @R1,R1 ;R1 -> active BBR MOV BB.BUF(R1),R5 ;R5 -> end message (ONLINE) MOV BB.RNG(R1),R4 MOV BB.POR(R1),R3 .HP1EN ADD #BB.RCT,R1 ;R1 (UMY) -> RCT size entry PUSH MOV P.RCTS(R5),R2 ;Store RCT size MOV P.TRCK(R5),R3 ;Store LBNs/Track MOV P.RBNS(R5),R4 ;Store RBNs - RCT copies .HP1EX P1UMY MOV R2,(R1)+ .ASSUME BB.RCT EQ 6 MOV R3,(R1)+ .ASSUME BB.TRK EQ 10 MOV R4,@R1 .ASSUME BB.RBN EQ 12 .ASSUME BB.COP EQ 13 .HP1EN POP .ASSUME UF.BBR EQ 100000 TST P.UNFL(R5) ;does controller support BBR itself? BMI 10$ ;branch if it does, otherwise do a ;a get unit status to record the BIT #UF.WPR,P.UNFL(R5) ;Is it write protected ? BNE 10$ ;branch if the unit is write protected MOV #ONLBBR,-(SP) ;use UMYJMP to enter the BBR psect $REL .-2 ONLBBR UMY JMP UMYJMP 10$: CALL GETNEXT ;because IGETUS is an internal ;operation with no recovery CALLR DISPAT ;Continue happily. .DSABL LSB .IF EQ DU$ERL-2 .SBTTL PAKMOD - *BBR* Check Host BBR datagrams ;+ ; PAKMOD - See if a datagram relevent to the Host BBR I/O needs to be ; modified. If the Event code is Data Error, then change the event ; code to Media Format Error. ; ; INPUT: R5 -> to the MSCP datagram ;- .ENABL LSB PAKMOD: MOV R0,-(SP) BISB #LF.RCT,L.FLGS(R5) ;set the error during replacement flag MOV L.EVNT(R5),R0 BIC #^cST.MSK,R0 ;Isolate the status/event code CMP #ST.DAT,R0 ;if it is not a data error BNE 10$ ;then done. else change it to a BIC #^cST.MSK,L.EVNT(R0) ;media format error BIS #ST.MFI,L.EVNT(R0) 10$: MOV (SP)+,R0 RETURN .DSABL LSB .ENDC ;EQ DU$ERL-2 .SBTTL GO2UMY - transfer control from UMX to UMY ; GO2UMY - this where we arrive when the UMX psect transfers control to ; the UMY psect. it is intended that UMY is entered only from UMX and ; UMY returns back to only UMX. ; ; GO2UMY cleans up after the a P1EXT invocation ;************************************************************************** .PSECT UMY ;************************************************************************** .ENABL LSB GO2UMY::MOV 2(SP),SP ;restore stack from P1EXT CMP (SP)+,(SP)+ ;pop the values return by P1EXT MOV (SP)+,R0 ;restore R0 MOV (SP)+,P1UMX ;store the UMX mapping for return MOV (SP)+,P1LW2 ;store low mem par for low mem refs RETURN ;this Return transfers control to ;to the desired destination, to the ;location DESTIN (see JMPUMY, above) .DSABL LSB .SBTTL UMYRET - *BBR* transfer control back to UMX, from UMY ;+ ; UMYRET - this where we goto when we want to transfer control from the ; UMY psect back to the UMX psect. it is intended that UMY is entered only ; from UMX and UMY returns back to only UMX. ; ; Input: if the caller wants to go to routine DESTIN in UMX, the caller ; does this ; ; MOV #DESTIN,-(SP) ; $REL .-2 DESTIN UMX ; JMP UMYRET ; ; UMYRET uses P1EXT to transfer control to the UMX routine GO2UMX. It ; is neccessary to us P1EXT because both UMY and UMX are in PAR1 memory ; regions. .ENABL LSB UMYRET:: .IF NE MMG$T MOV @#KISAR1,-(SP) ;save the current PAR1 bias .YP1EX P1UMX ;map to UMX JMP @#GO2UMX $REL .-2 GO2UMX UMX .YP1EN .IFF ;NE MMG$T JMP @(SP)+ .ENDC ;NE MMG$T .DSABL LSB .SBTTL BBR - *BBR* Host Bad Block Replacement Main Entry Points .SBTTL BBFND - *BBR Step 1-2* Bad Block Found From Interrupt Error Code. ;+ ; The following routine provides the entry points for the start of ; processing the re-vectoring of bad blocks; the following three entry ; points exists! ; ; BBFND - A bad block has been found from the interrupt error code. ; ; ONLBBR - Entry point as a result of executing an ONLINE for the ; drive. ; ; CONBBR - Continue BBR - this is entered from the interrupt routine ; when it is detected that BBR processing is on. ;- ;***************************************************************************** ; STEP 1 - 2 ;***************************************************************************** ;* In the following algorithm these terms are used: ;* ;* Phase 1 - the determination if the block is really bad. ;* Phase 2 - the replacement of the block if it is indeed bad. ;* Sector 0,1,etc. - sectors relative to the start of an RCT copy. ;* ;* All operations are performed with error correction and error ;* recovery enabled unless noted otherwise. ;* ;* Since the RCT is a multicopy structure, all READ and WRITE ;* operations performed within the RCT are Multiread or Multiwrite ;* operations. ;* ;* Bad Host Applications space LBNs are to be replaced using the ;* following procedure. ;* ;* 1. The bad block replacement process is notified that a ;* specified logical block is bad. The notification ;* includes the bad block's LBN. ;* ;* Alternatively, the bad block replacement process is ;* notified that a failure or loss of context occurred in ;* the middle of bad block replacement. Failure or loss of ;* context is detected while bringing the unit online when ;* the online process reads RCT block 0 and notices one of ;* the Phase Flags is set. The online process will ;* retrieve the following information from RCT block 0 and ;* 1 and supply it to the BBR handling routine. ;* ;* If the failure or loss of context occurred in phase 1 or ;* 2 of bad block replacement, the notification includes: ;* ;* o The bad block's LBN, ;* ;* o The previous data contents of the bad block, ;* ;* o And whether or not the data is valid (i.e., whether ;* or not the original read of the bad block yielded ;* usable data, as indicated by the FE flag in RCT ;* block 0) ;* ;* If the failure or loss of context occurred during phase ;* 2 of bad block replacement then the notification also ;* includes: ;* ;* o The new RBN with which to replace the bad block, ;* ;* o Whether or not the bad block has previously been ;* replaced, ;* ;* o And (if it has previously been replaced) the old RBN ;* that currently replaces the bad block. ;* ;* ;* 2. The bad block replacement process locks all access to ;* the bad block and all update or write access to the ;* unit's RCT. This is a soft lock - i.e., the lock is ;* implicitly released if the host loses context (crashes). ;* Given the expected low frequency of bad block ;* replacement, most implementations may choose to lock out ;* all access to the entire unit, rather than just locking ;* the bad block and RCT. But since RT11's DU handler is not ;* internally queued, and since invocation of the BBR code ;* suspends all of the handler's other activity, this requirement ;* is implicitly satisfied. ;* ;* The process then initializes the BBR RECURSION COUNTER ;* to 0. This counter is used in step 9 to inhibit ;* infinitely looping through the replacement procedures ;* and exhausting the RBN supply. ;* .ENABL LSB BBFND:: CLR BBRECU ;Clear recursion counter MOV ACTBBR,R2 ;R2 -> BBR control block TST @R2 ;Controller supports BBR ? BMI 30$ ;Yes, branch; no host BBR CALL CLRWRK ;Go clear work area! MOV ACTBBR,R5 ;get the address of the end message MOV BB.BUF(R5),R5 ;with the reported bad block .YP1EX P1UMX MOV P.FBBK(R5),R2 ;copy the reported bad block to MOV P.FBBK+2(R5),R3 ;registers which are preserved across .YP1EN ;P1EXT MOV R2,BLBNL ;Store bad block low MOV R3,BLBNH ;Store bad block high 10$: .YP1EX P1LW2 ;Start of P1EXT block MOV @#STATU$,R1 ;Save old status $REL .-2 STATU$ UYR ;($REL knows about XM vs. FB) .YP1EN ;End of P1EXT block MOV R1,OLDSTA ;Store the old status CALL GETMES ;save original end message MOV ACTBBR,R5 ;that invoked BBR. MOV BB.POR(R5),R3 ;R3 -> port control table entry for MOV BB.RNG(R5),R4 ;unit. R4 -> to the ring. MOV BB.BUF(R5),R5 ;R5 -> the buffer with the end packet MOV #GETNEXT,R1 $REL .-2 GETNEXT UMX ;$REL knows about XM vs. FB .YP1EX P1UMX ;We must call GETNEXT this releases CALL @R1 ;the end message with the BBR flag on .YP1EN ;We would like to execute the next line, the DO macro ;DO IGETUS,THEN,CKWRTP ;- but we cannot, because we are in the UMY psect, and ;and the DO macro only works in the UMX psect. so intsead, we ;must do the following MOV #WRPUMX,-(SP) $REL .-2 WRPUMX UMX JMP UMYRET ;we will come back to here - if the unit is not write protected ;RCONTX will perform the GETNEXT CKWRTP: MOV ACTBBR,R2 ;R2 -> BBR control block MOV BB.BUF(R2),R5 .YP1EX P1UMX MOV P.UNFL(R5),R5 .YP1EN MOV OLDSTA,R4 ;R4 = old status BIT #UF.WPR,R5 ;Write protected? BNE 20$ ;branch if yes .IF EQ DU$ERL-2 CALL INITPK ;initialize host constructed datagrams MOV BLBNL,BBRPAK+L.LBN ;move the bad LBN into the datagram MOV BLBNH,BBRPAK+L.LBN+2 .ENDC ;EQ DU$ERL-2 CALLR CONBBR 20$: MOV ACTBBR,R5 MOV BB.POR(R5),R3 MOV BB.RNG(R5),R4 MOV BB.BUF(R5),R5 CALL RETMES ;stuff original end message into ;current buffer ;When we return to SKPBBR, R3,R4, and R5 must be restored 30$: MOV #SKPBBR,-(SP) ;couldn't enter BBR because of $REL .-2 SKPBBR UMX ;write protection JMP UMYRET ;+ ; Clear work area routine ;- CLRWRK: MOV #XFRSZ,R3 ;R3 = address of work area $REL .-2 XFRSZ UMY MOV #WRKSIZ/2,R4 ;R4 = size of work area 40$: CLR (R3)+ ;Clear location SOB R4,40$ ;Are we finish? RETURN ;+ ; Online entry! ;- ; On Entry: We are mapped to DUADDR. ??? what does this mean ??? ONLBBR::MOV ACTBBR,R2 ;R2 -> active BBR table CALL CLRWRK ;Go clear work area BIS #US.OLP,USTAT ;Indicate ONLINE .IF EQ DU$ERL-2 CALL INITPK ;initialize host constructed datagrams .ENDC ;EQ DU$ERL-2 .BR CONBBR ;+ ; Entry from interrupt! detection of process on! ;- ; R2 -> ACTBBR CONBBR::MOV SP,(PC)+ ;Store the current stack SAVESP: .WORD 0 ;Storage location CALL RCONTX ;Restore context if any MOV ACTBBR,R2 ; If there was any context - we will go there... (not return back to here) CLR BBRECU ;Clear recursion flag .BR DOBBR ;Go do bad block replacement .DSABL LSB .SBTTL DOBBR - *BBR Step 3* Read RCT0: Get Flags, Bad LBN, etc. in Memory ;**************************************************************************** ; STEP 3 ;**************************************************************************** ;* 3. If the replacement process is recovering from a failure ;* or loss of context that occurred during phase 1 of bad ;* block replacement, then go to step 7. ;* ;* If the replacement process is recovering from a failure ;* or loss of context that occurred during phase 2 of bad ;* block replacement, then remember the fact for step 12 ;* and go to step 11. ;* ;* Otherwise continue with step 4. ;+ ; Read block RCT0 so we have flags, bad LBN, etc., in memory. ;- .ENABL LSB DOBBR: .IF NE DU$BRD .ADDR #S3,R0 .PRINT .ENDC CALL MULRDB ;Setup & read sector 0 of table BCC 10$ ;If CC, then ok ;serious error!!!! .IF EQ DU$ERL-2 ;log disk transfer error (DTE datagram) MOV #DTEPAK,R0 $REL .-2 DTEPAK UMY MOV #MF.RCI,L.EVNT(R0) CALL DO.DTE .ENDC ;EQ DU$ERL-2 ;Software Write Lock the Disk ;print disaster message CALLR TROUBLE ;Failed to read sector 0 10$: BIT #US.OLP,USTAT ;Unit being brought online? BEQ READIT ;No, skip the rest! ;write sector 0: success means we can assume, below, that we ;have just successfully read and written sector 0 CALL MULWTB BCC 40$ ;serious error!!!! .IF EQ DU$ERL-2 ;log disk transfer error (DTE datagram) MOV #DTEPAK,R0 $REL .-2 DTEPAK UMY BISB #LF.RCT,L.FLGS(R0) ;say Error During Replacement CMPB IOST,#ST.DAT BEQ 20$ MOV IOST+2,L.EVNT(R0) BR 30$ 20$: MOV #MF.RCI,L.EVNT(R0) ;say Media Format Error 30$: CALL DO.DTE .ENDC ;EQ DU$ERL-2 ;Software Write Lock the Disk ;print disaster message CALLR TROUBLE ;Failed to write sector 0 40$: ;+ ; ?????????? Read sector 0 of each replacement control table copy. ; If replacing a bad block, update the contents and write it back to sector 0. ; If bringing a unit online, check for partial replacement recovery and ; Write-back cache corruption. ??????????????????????? ;- .IF NE DU$ERL-2 CALL INTEGA ;Go do some integrity checks ;NOTE: INTEGA sets R0 -> sector 0 RCT flag word ;do not have enough room for INTEGA when error logging is built BCC 50$ ;If C=0, then all ok ;set up BBR flags .IFF ;NE DU$ERL-2 MOV #SEC0+RCTFLG,R0 $REL .-2 SEC0+RCTFLG UMY BR 50$ .ENDC ;NE DU$ERL-2 CALLR TROUBLE ;RCT block 0 is trash 50$: BIT #,@R0 ;BBR algorithm interrupted? ;(already checked both bits aren't on) BNE 60$ ;branch if yes CALLR STEP18 ;Goto Step 18. Since we are currently ;bringing a unit online, if we are ;here step18 will goto BEGIN in UM.MAC 60$: ; Do the following code if recovering from PHASE 1 ***OR*** from PHASE 2 ; In either case do the following: (r0 currently -> SEC0+RCTFLG) MOV RLBNL-RCTFLG(R0),BLBNL ;Stuff the saved bad LBN low MOV RLBNH-RCTFLG(R0),BLBNH ; and the high bits .IF EQ DU$ERL-2 MOV ACTBBR,R2 ;because we are resuming an ;interrupted BBR process, CLR BB.COD(R2) ;we do not have the status/event ;code from the end message that ;originally observed the Bad Block ;Detected flag as set - zero indicates ;this situation MOV BLBNL,BBRPAK+L.LBN MOV BLBNH,BBRPAK+L.LBN+2 .ENDC ;EQ DU$ERL-2 ;bit2 of INTFLG must reflect the RF.FE bit in Sector 0 BIC #BIT2,INTFLG ;init bit #2 to off BIT #RF.FE,SEC0+RCTFLG ;is the forced error bit in RCT set? BEQ 70$ ;branch if not BIS #BIT2,INTFLG ;set bit #2 of INTFLG to on 70$: ; Read RCT sector 1 into LBN for PHASE 1 recovery MOV ACTBBR,R2 MOV BB.MAX(R2),LBNL ;Table sector 0 LBN, Low ord MOV BB.MAX+2(R2),LBNH ;Table sector 0 LBN, Hi ord ADD #1,LBNL ;Table sector 1 LBN, Low ord ADC LBNH ;Table sector 1 lbn, Hi ord MOV #LBN,DSKBF ;Read into LBN $REL .-4 LBN UMY ;Up up and away CALL MULRDC ;Go do read (COMPARE) of sector 1 BCC 80$ ;read of sector 1 failed - couldn't get the block (i.e., sector 1) ;which is used to hold the data from the original bad block .IF EQ DU$ERL-2 ;log disk transfer error (DTE datagram) MOV R0,-(SP) MOV #DTEPAK,R0 $REL .-2 DTEPAK UMY BISB #LF.RCT,L.FLGS(R0) ;say error during replacement MOV #MF.RCI,L.EVNT(R0) ;say media format error CALL DO.DTE MOV (SP)+,R0 .ENDC ;EQ DU$ERL-2 ;set the FE bit in sector 0, write sector 0 with MULWTB (which ;just worked successfully so it should work immediately again) BIS #RF.FE,@R0 CALL MULWTB 80$: BIT #RF.P1,@R0 ;Recover from PHASE 1 ? BEQ 90$ ;No, so go do PHASE 2 rec CALLR ALLOUT ;Goto step 7 ; Extra recovery for PHASE 2 begins here ; these moves are accomplished by P2REC ; MOV 10(R0),NRBNL ;Stuff saved RBN low ; MOV 12(R0),NRBNH ; and stuff the high RBN ; MOV 14(R0),ORBNL ;Stuff saved Old RBN Low ; MOV 16(R0),ORBNH ; and stuff the High RBN ; The following call will set up the old primary RBN block and offset ; which are used in Step 11 to calculate if RBN is primary or not. ; The values above are calculated in P2REC, which calls HASH. 90$: CALL P2REC ;do phase 2 recovery BIS #BIT4,INTFLG ;Remember we've been here CALLR RCTUPD ;Goto Step 11 .DSABL LSB .SBTTL READIT - *BBR Step 4* Read the Data from the Bad LBN ;**************************************************************************** ; STEP 4 ;**************************************************************************** ;* 4. The process clears (zeros) a sector sized buffer, then ;* reads the current contents of the bad block into the ;* buffer. ;* ;* o The buffer is cleared first for the rare case when ;* no data can be transferred (such as a valid sync ;* pattern is not detected). ;* ;* o Then up to 4 reads are performed with error recovery ;* and error correction enabled to attempt to recover ;* the data. The read is successful if no errors are ;* detected or only a forced error is detected. In ;* addition to saving the data, the process remembers ;* whether or not the read succeeded. This information ;* will be used in Step 6 in determining whether to set ;* or clear the FE flag in RCT block 0. ;* ;* If a forced error is encountered, remember the fact ;* for Step 6. ; ; Read the data from the bad LBN .ENABL LSB READIT: .IF NE DU$BRD .ADDR #S4,R0 .PRINT .ENDC MOV #4,R4 ;Try four times ... 10$: MOV #LBN,R3 ;Get buffer address $REL .-2 LBN UMY MOV R3,DSKBF ;Set buffer address MOV #256.,R0 ;Get number of words to zero 20$: CLR (R3)+ ;Clear out the buffer SOB R0,20$ ;Done? MOV BLBNL,LBNL ;LBNL = LBN to read, low order MOV BLBNH,LBNH ;LBNH = LBN to read, high order MOV #OP.RD,IOFUN ;Set function code MOV #MD.CMP,IOMOD ;Compare modifier BIS #BIT1,INTFLG ;Assume reads won't work MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Read the bad LBN MOV (SP)+,R2 ;*C* MOV doesn't disturb the carry bit BCS 30$ ;Branch if error BIC #BIT1,INTFLG ;Remember read worked ;Checked in Step 6 BR WRITIT ;Branch ; Still succeed if only forced error. 30$: CMP #ST.DAT,IOST+2 ;Only FE for full status ? BEQ WRITIT ;Yes, then its a good read SOB R4,10$ ;Yes, lets retry... ;Before flaggin FE error! .BR WRITIT .DSABL LSB .SBTTL WRITIT - *BBR Step 5* Write Data to Sector 1 of Each RCT Copy ;**************************************************************************** ; STEP 5 ;**************************************************************************** ;* 5. The process records the data obtained when the bad block ;* was read during step 4 in sector 1 of the RCT. Note ;* that the Multiwrite algorithm used to record the data in ;* sector 1 uses write-compare operations to guarantee that ;* the data is successfully recorded. The data must NOT be ;* written with the forced error modifier or the RCT will ;* appear corrupted if sector 1 is read again by the ;* multi-copy read. ;* ;* If either the Multiwrite fails, report the ;* BBR Completion with the Inconsistent RCT event subcode, the ;* Error During Replacement error log message flag set, and ;* the Inconsistent RCT replace flag set. ;* Report the error to the error log and go to step 18. ;* ; ; Write the data to sector 1 of each replacement control table copy ; .ENABL LSB WRITIT: .IF NE DU$BRD .ADDR #S5,R0 .PRINT .ENDC MOV ACTBBR,R2 MOV BB.MAX(R2),LBNL ;Table sector 0 LBN, Low order MOV BB.MAX+2(R2),LBNH ;Table sector 0 LBN, Hi order ADD #1,LBNL ;Table sector 1 LBN, Low orde ADC LBNH ; ... and High order CALL MULWTC ;Multi-copy write sector 1 (Compare) BCC PHASE1 ;If CC ok .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIT #BIT8,INTFLG ;non primary replacement? BEQ 10$ BIS #LFR.TE,L.RPFL(R0) 10$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CALLR STEP18 ;Failure, can not write any .ENABL LSB .SBTTL PHASE1 - *BBR Step 6* Update RCT Sector 0 to Reflect Phase 1 State ;**************************************************************************** ; STEP 6 ;**************************************************************************** ;* 6. The process updates RCT sector 0 to reflect the phase 1 ;* state. This requires reading sector 0, modifying the ;* appropriate flags, then writing the updated sector 0 in ;* order to preserve other information stored in this ;* sector. The following is recorded: ;* ;* o The bad block's LBN, ;* ;* o Whether or not the saved data is valid (the FE bit ;* is set if forced error was detected in the Step 4 ;* READ or if the Step 4 READ did not succeed), ;* ;* o And set the Phase 1 indicator (the P1 bit) in sector ;* 0 of the RCT. ;* ;* If either the Multiread or Multiwrite fails, report the ;* BBR Completion with the Inconsistent RCT event subcode, the ;* Error During Replacement error log message flag set, and ;* the Inconsistent RCT replace flag set ;* ;* If sector 0 cannot be successfully read, report the ;* error to the error log and go to step 18. ;* ;* If sector 0 cannot be successfully written, report the ;* error to the error log and go to step 17. ;* .ENABL LSB ; ; R0 -> Sector 0 - CRTFLG flag (PAR1 address) ; ; Set up the call to read SECTOR 0 into SEC0: PHASE1: .IF NE DU$BRD .ADDR #S6,R0 .PRINT .ENDC CALL MULRDB ;Setup & read sector 0 BCC 20$ ;No problem, continue .IF EQ DU$ERL-2 CALL DTEMOD ;send a DTE packet MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIT #BIT8,INTFLG ;non primary replacement? BEQ 10$ BIS #LFR.TE,L.RPFL(R0) 10$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CALLR STEP18 ;Get out at step 18 20$: BIS #RF.P1,SEC0+RCTFLG ;Indicate phase 1 BIC #RF.FE,SEC0+RCTFLG ;Clear out FE bit in SEC0 BIT #BIT1,INTFLG ;Want to set FE in SEC0 ? BEQ 30$ ;No branch BIS #RF.FE,SEC0+RCTFLG ;Set Forced Error bit in SEC0 BIC #BIT1,INTFLG ;Clear flag for FE (set step4) 30$: MOV #SEC0+RLBNL,R0 ;R0 = LBN being replaced (lo order) $REL .-2 SEC0+RLBNL UMY ;... MOV BLBNL,(R0)+ ;Record bad LBN in sect 0 bf MOV BLBNH,(R0)+ ;... and the high bits ; -- Here, Phase 1 flag is written to RCT CALL MULWTB ;Setup & write updated sect 0 to disk BCC ALLOUT ;Go for the allout test .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIT #BIT8,INTFLG ;non primary replacement? BEQ 40$ BIS #LFR.TE,L.RPFL(R0) 40$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CALLR STEP17 ;Go to failure exit .DSABL LSB .SBTTL ALLOUT - *BBR Step 7* Test Block: Rd/Wrt w/Err Correct & Recovery Off ;**************************************************************************** ; STEP 7 ;**************************************************************************** ;* 7. The process uses the Write and Read commands, with error ;* correction and error recovery DISABLED, to test the ;* block in question. If any Write or Read operation fails ;* during any of the following tests, remember that fact ;* for later steps (8) and exit at step e below. The test ;* procedure includes the following: ;* ;* a. Read the bad block 4 times, unless an error occurs, ;* to determine if the original contents contain an ;* error. An error has occurred if the returned status ;* code is either Success with the BBR flag asserted or ;* if the status code is Data Error with a nonzero ;* subcode. If an error is detected go to step e. ;* ;* b. Write the saved data followed with 4 Reads to stress ;* test the block in question. ;* ;* An error has occurred on the Write if the returned ;* status code is not Success. An error has occurred ;* on the Read if the returned status code is either ;* Success with the BBR flag asserted or if the status ;* code is Data Error with a nonzero subcode. If an ;* error is detected during the write or reads go to ;* step e. ;* ;* c. Write the inversion (1's complement) of the saved ;* data followed by 4 Reads to stress test the block in ;* question. The inversion of the saved data will be ;* written with the Forced Error Modifier to protect ;* the integrity of the block. An error has occurred ;* on the Write if the returned status code is not ;* Success. An error has occurred on the Read if the ;* returned status code is Success or if the status ;* code is Data Error with a nonzero subcode. If an ;* error is detected go to step e. ;* ;* d. Repeat step b. and c. 8 times, unless an error ;* occurs. ;* ;* e. Go to the next step to put the saved data back in ;* the bad block. This will ensure that disk integrity ;* can survive power fail and unexpected drive errors ;* which may occur in subsequent steps. ;* ; Repeat this whole procedure 8 times or till an error occurs. .ENABL LSB ALLOUT: .IF NE DU$BRD MOV R0,-(SP) .ADDR #S7,R0 .PRINT MOV (SP)+,R0 .ENDC MOV #8.,R5 ;Load count BIC #BIT3,INTFLG ;Assume no errors yet ;Read four times RTEST: MOV #4,R4 ;Intermediate count 10$: MOV #RCTBF,DSKBF ;Store buffer address $REL .-4 RCTBF UMY MOV #OP.RD,IOFUN ;Store function code MOV #,IOMOD ;Store modifiers MOV BLBNL,LBNL ;Store block to read low MOV BLBNH,LBNH ;Store the high block MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Go do read MOV (SP)+,R2 TST IOST+2 ;Success ? BNE 20$ ;No, branch .ASSUME ST.SUC EQ 0 BIT #100000,IOST ;BBR flag asserted ? BEQ 30$ ;No, no error, branch BIS #BIT3,INTFLG ;Set error bit CALLR SCAN ;Goto next step (8) 20$: CMP #ST.DAT,IOST+2 ;Data error? BEQ 30$ ;No, no error, so continue BIS #BIT3,INTFLG ;Set error bit BR SCAN ;Goto next step (8) 30$: SOB R4,10$ ;Are we finished ? repeat if not... ;Write saved data - then read 4 times RWTEST: MOV #OP.WR,IOFUN ;Store function MOV #LBN,DSKBF ;Store buffer addr to write $REL .-4 LBN UMY MOV #,IOMOD ;Store modifiers MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Go write it! MOV (SP)+,R2 BCC 40$ ;No error, branch BIS #BIT3,INTFLG ;Set error bit BR SCAN ;Goto next step (8) 40$: MOV #4,R4 ;Load read count 50$: MOV #OP.RD,IOFUN ;Say read MOV #RCTBF,DSKBF ;Load buffer to read to $REL .-4 RCTBF UMY MOV #,IOMOD ;Store modifiers MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Go do it! MOV (SP)+,R2 TST IOST+2 ;Success ? BNE 60$ ;No, branch .ASSUME ST.SUC EQ 0 BIT #100000,IOST ;BBR flag asserted ? BEQ 70$ ;No, no error, branch BIS #BIT3,INTFLG ;Set error bit BR SCAN ;Goto next step (8) 60$: CMP #ST.DAT,IOST+2 ;Data error with F.E. sub ? BEQ 70$ ;Yes, no error BIS #BIT3,INTFLG ;Set error bit BR SCAN ;Goto next step (8) 70$: SOB R4,50$ ;Are we finish? repeat if not... ;Write 1's complement - read 4 times! TSTPAT: MOV #256.,R4 ;R4 = no. of entries in buff MOV #RCTBF,R0 ;R0 = ofset to buffer 1's $REL .-2 RCTBF UMY ;Use saved data - not what we've just ; read in, but what was read in STEP 4 MOV #LBN,R3 ;R3 = offset bad block data $REL .-2 LBN UMY 80$: MOV (R3)+,@R0 ;Store word COM (R0)+ ;1's complement SOB R4,80$ ;Done? branch if not... MOV #OP.WR,IOFUN ;Set write code MOV #,IOMOD ;Modify (FE) MOV #RCTBF,DSKBF ;Use buff of 1's comp $REL .-4 RCTBF UMY ;Remember to use high addr MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Write the test pattern MOV (SP)+,R2 BCS SCAN ;Branch if *NOT* data error .BR 90$ ; Data errors are factored out in DSKIO, if the modify word ; had its bits set for Forced Error. So coming back, if CARRY is SET ; then something else besides FE was flagged. ; Now lets read (Note: same modifiers, same buffer) four times! 90$: MOV #4,R4 ;Repeat count 100$: MOV #,IOMOD ;Modify (FE) MOV #OP.RD,IOFUN ;Read code MOV #RCTBF,DSKBF ;Use test buffer $REL .-4 RCTBF UMY ;Up up and away MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Read the test pattern MOV (SP)+,R2 TST IOST+2 ;Status a success ? BNE 110$ ;No, branch BIS #BIT3,INTFLG ;Set error bit BR SCAN 110$: CMP #ST.DAT,IOST+2 ;Data error with FE subcode ? BEQ 120$ ;Yes, alright. No error BIS #BIT3,INTFLG ;Set error flag. BR SCAN ;Goto next step (8) 120$: DEC R4 ;Should we try again ? BNE 100$ ;Yes, branch DEC R5 ;Are we completley finished? BNE RWTEST ;No - there we go again! ;Do steps b + c 8 times ! .BR SCAN ;Now go to step 8 .DSABL LSB .SBTTL SCAN - *BBR Step 8* Write Saved Data Back to Bad LBN ;**************************************************************************** ; STEP 8 ;**************************************************************************** ;* 8. The process writes the saved data back out to the bad ;* block using a Write operation. The write is performed ;* with the forced error modifier if the saved data is ;* invalid (i.e., if the FE bit is set in the ;* memory-resident copy of RCT block 0). The Write ;* succeeds if the returned status code is Success. ;* ;* If the Write succeeds, it is followed by a Read ;* addressed to the bad block's LBN. The Read succeeds if ;* the returned status code is Success or if the status ;* code is Data Error with subcode Forced Error AND the ;* Write was performed with the Forced Error modifier. If ;* the Read succeeds, then the data read is compared with ;* the data written. The operation succeeds if the data is ;* the same. ;* ;* If any Write, Read, or compare operations failed in ;* Steps 7 or 8, go to the next step, otherwise go to step ;* 13. ;* ; .ENABL LSB SCAN: .IF NE DU$BRD .ADDR #S8,R0 .PRINT .ENDC CALL WTSAV ;Write saved data to bad LBN BCC 10$ ;Write worked, continue BIS #BIT3,INTFLG ;Remember error BR STEP9 ;Goto next step (9) ; Read the LBN 10$: MOV BLBNL,LBNL ;Move bad block number MOV BLBNH,LBNH ;And hi word MOV #RCTBF,DSKBF ;Setup buffer to receive $REL .-4 RCTBF,UMY ;Up up and away ; Not using buffer "LBN" because we'll have to save this to compare to when ; we do the write. MOV #OP.RD,IOFUN ;Say to read CLR IOMOD ;No modifiers BIS #BIT5,INTFLG ;Assume they don't match MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Go do read MOV (SP)+,R2 TST IOST+2 ;Success ? BEQ 20$ ;Yes, go compare data CMP #ST.DAT,IOST+2 ;Data error with FE ? BNE 50$ ;No, then definite error BIT #RF.FE,SEC0+RCTFLG ;Was Forced Error bit on ? BEQ 50$ ;No, error ; Now compare what was written with what was read 20$: PUSH ;Save R0-R2 MOV #BLKLEN,R0 ;R0=counter # words MOV #LBN,R1 ;R1 ->read's buffer $REL .-2 LBN UMY ;Up, up and away MOV #RCTBF,R2 ;R2 ->write's buffer $REL .-2 RCTBF UMY ;Up, up and away 30$: CMP (R1)+,(R2)+ ;Compare data BNE 40$ ;Not equal SOB R0,30$ ;More words to compare? branch if yes BIC #BIT5,INTFLG ;Clear flag for success 40$: POP ;Restore registers 50$: BIT #,INTFLG ;Either error bit on ? BEQ 60$ ;No, branch to STEP13 BIC #,INTFLG ;Clear flags BR STEP9 ;Goto next STEP (9) 60$: BIS #BIT7,INTFLG ;tell step 13 that step 8 jumped to ;step 13 CALLR RESDAT ;Goto STEP 13 .DSABL LSB .SBTTL SCAN1 - *BBR Step 9* Scan RCT and Determine RBN ;**************************************************************************** ; STEP 9 ;**************************************************************************** ;* 9. This step consists of two sub-steps: ;* ;* a. The process checks a BBR RECURSION COUNT to see if ;* this step has been executed 2 times indicating that ;* 2 RBNs were found bad during the Write and Compare ;* operation in Step 12C and/or Read operation in Step ;* 12A. ;* ;* o If BBR RECURSION COUNT is = 2 then report a ;* replacement failure code 2 and the BAD RBN FLAG ;* to the error log and go to step 16. ;* ;* o If BBR RECURSION COUNT is < 2 then ;* count=count+1. ;* ;* ;* b. If a failure is detected in step 7 or 8 the process ;* scans the RCT, using the RCT Search algorithm ;* (Section 6.5), and determines the following: ;* ;* o The replacement RBN, which is determined by ;* finding an empty descriptor before completing ;* the RCT search. Only the CODE portion of the ;* RBN descriptor can be tested when determining ;* the disposition of the RBN, testing differently ;* will have unpredictable results. ;* ;* o Whether or not the bad block was previously ;* replaced, which indicates the replacement is for ;* an RBN. This condition is detected when the bad ;* block's LBN matches the RBN descriptor currently ;* being tested. ;* ;* o If the LBN had been previously replaced, the ;* block's old RBN is saved. The old RBN will be ;* used as a pointer to the RBN descriptor to mark ;* unusable in step 11. ;* ;* If the RCT search fails, report the error to the ;* error log and go to step 16. An RCT search failure ;* is detected when all the descriptors have been ;* tested and no unused descriptor can be found, all ;* copies of the same relative RCT block cannot be ;* read, or the RCT is found to be corrupt (see Section ;* 6.5 for RCT result codes). ; ; Scan Replacement Control Table and determine the RBN .ENABL LSB STEP9: SCAN1: .IF NE DU$BRD .ADDR #S9,R0 .PRINT .ENDC CMP #2,BBRECU ;Have we found a 2nd bad RBN? BNE 30$ ;No, branch .IF EQ DU$ERL-2 MOV #BBRPAK,R0 $REL .-2 BBRPAK UMY MOV #BB.TWO,L.EVNT(R0) ;recursion failure event code BISB #LF.RCT,L.FLGS(R0) ;error during replacement flag BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIS #LFR.BR,L.RPFL(R0) ;Bad RBN flag 10$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 20$ ;branch if not BIS #LFR.TE,L.RPFL(R0) 20$: CALL DO.BBR .IFF ;EQ DU$ERL-2 10$: .ENDC ;EQ DU$ERL-2 CALLR STEP16 ;Yes, skip - failure 30$: INC BBRECU ;Update pass thru here. CALL HASH ;Fnd where to search CALL SRCH ;Search the table for the RBN BCS 10$ ;Search failed, goto step16 ;NOTE: if we branch, then the BBR packet was defined ; by SRCH .BR PHASE2 ;Goto next step (10) .DSABL LSB .SBTTL PHASE2 - *BBR Step 10* Update RCT Sector 0 to Reflect Phase 2 State ;**************************************************************************** ; STEP 10 ;**************************************************************************** ;* 10. The process updates the RCT sector 0 to reflect the ;* Phase 2 state. The RCT must be updated without reading ;* sector 0, instead using the copy of sector 0 last read ;* from or written to the RCT. The following is Recorded: ;* ;* o The new RBN, ;* ;* o Whether or not the bad block has been previously ;* replaced (the BR bit), ;* ;* o The bad block's old RBN (if it has been previously ;* replaced), ;* ;* o And the fact that we are in Phase 2 of Bad Block ;* Replacement (the P2 bit) in sector 0 of each RCT ;* copy. ;* ;* If the RCT cannot be updated, report the error to the ;* log and go to step 16. ;* ; ; Update Sector 0 on the disk with the fact that we are in Phase 2 ; .ENABL LSB PHASE2: .IF NE DU$BRD .ADDR #S10,R0 .PRINT .ENDC BIC #RF.P1,SEC0+RCTFLG ;Show we've gone from phase 1 BIS #RF.P2,SEC0+RCTFLG ;... to phase 2 MOV #SEC0+URBNL,R0 ;R0 -> new RBN save area.. $REL .-2 SEC0+URBNL UMY .ASSUME URBNL+4 EQ RRBNL .ASSUME URBNH+4 EQ RRBNH .ASSUME URBNH+2 EQ RRBNL MOV NRBNL,(R0)+ ;Record new RBN in sector 0 MOV NRBNH,(R0)+ ;... MOV ORBNL,(R0)+ ;Record old RBN (if any) MOV ORBNH,@R0 ;... BNE 10$ ;if ORBN(L,H) is not zero, then there TST ORBNL ;is an old replacement block BEQ 20$ ;Definitely is not an old RBN 10$: BIS #RF.BR,SEC0+RCTFLG ;Set bad RBN flag ; -- RCT phase 1 flag cleared, phase 2 flag set 20$: CALL MULWTB ;Setup & wrt updated sect 0 back->disk BCC RCTUPD ;Good write, do RCT update .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG BEQ 30$ BIS #LFR.BR,L.RPFL(R0) 30$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 40$ BIS #LFR.TE,L.RPFL(R0) 40$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CALLR STEP16 ;Go to faIlure exit, could not ; write any of the SEC0 blocks .DSABL LSB .SBTTL RCTUPD - *BBR Step 11* Update RCT to Permanently Record RBN ;**************************************************************************** ; STEP 11 ;**************************************************************************** ;* ;* 11. The process updates the RBN descriptor(s) in the RCT to ;* permanently record the replacement. If this requires ;* updating two blocks in the RCT, then both blocks must be ;* read before either is written. The following descriptor ;* updates can occur: ;* ;* o The descriptor associated with the new RBN will be ;* marked in use by writing the primary or non-primary ;* replacement indicator and the bad block's LBN to it. ;* ;* o The descriptor associated with the old RBN (if any) ;* is marked unusable. This can only occur if an RBN, ;* which was previously used for replacement, fails ;* step 7 testing or during step 12 we could not write ;* the saved data to the replacement RBN and had to ;* find another RBN candidate. ;* ;* ;* NOTE ;* ;* Marking an RBN descriptor unusable declares the ;* RBN to be bad. The only method of recovering ;* the RBN, from this point on, is to reformat the ;* media. ;* ;* ;* If a block cannot be read successfully, report the error ;* to the error log and go to step 16. ;* ;* If a block cannot be written successfully, report the ;* error to the error log and go to step 15. ;* ; ; Update Replacement Control Table with RBN ; .ENABL LSB RCTUPD: .IF NE DU$BRD .ADDR #S11,R0 .PRINT .ENDC ;Read into RCTBF sector w/ correct RBN CALL NEWBF ;Set paramet for new RBN I/O ;R0 now = RCTBF CALL MULRDC ;Rd (COMPARE) sec in table of new RBN BCS 40$ ;Read failed, restore bad LBN MOV R0,R3 ;Get buffer address ADD NLBNO,R3 ;Form RBN descriptor address MOV BLBNL,(R3)+ ;Set LBN address(lo)-in LBN MOV BLBNH,@R3 ;... and high order BIC #170000,@R3 ;Clear the high order four bits ;of the LBN number. The high order ;four bits are status indicators; see ;the DSDF spec. BIS #DF.ALL,@R3 ;Indicate new RBN is allocatd ;... it is primary CMP PLBNO,NLBNO ;Are offs to RBNS/RCTS LBN =? BNE 10$ ;No, must be secondary CMP PLBNL,NLBNL ;Primary RBN same as new RBN? BNE 10$ ;No CMP PLBNH,NLBNH ;Is it? BEQ 20$ ;No, it must be secondary 10$: BIS #DF.SEC,@R3 ;Indicate new RBN is secondary 20$: BIT #RF.BR,SEC0+RCTFLG ;Is there an old RBN? BEQ 90$ ;branch if no ; We are replacing an old RBN. CMP OLBNL,NLBNL ;Old RBN sector = new RBN sec? BNE 30$ ;If NE no CMP OLBNH,NLBNH ;Maybe, how about high order? BEQ 70$ ;Yes, they are the same ; The old and new RBN are in different RCT blocks 30$: CALL OLDBF ;Set parameters for old RBN MOV R0,DSKBF ;Set buffer address CALL MULRD ;Read sec in table of old RBN BCC 70$ ;If CC ok 40$: .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG BEQ 50$ BIS #LFR.BR,L.RPFL(R0) 50$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 60$ BIS #LFR.TE,L.RPFL(R0) 60$: CALL DO.BBR SEC .ENDC ;EQ DU$ERL-2 CALLR STEP16 ;Read failure, goto STEP16 70$: MOV R0,R3 ;Get buffer address ADD OLBNO,R3 ;Form RBN descriptor address MOV #DF.UNU,2(R3) ;Indicate old RBN is unusable CLR @R3 ;... MOV R0,DSKBF ;Set buffer address CALL MULWTC ;Write the old RBN sector back to ;the RCT with Compare enabled BCS 100$ ;If CC=0, then write ok 80$: CMP OLBNL,NLBNL ;Old RBN sec = new RBN sector? BNE 90$ ;branch if no CMP OLBNH,NLBNH ;Maybe, how about high order? BEQ REPLBN ;Yes, they are the same 90$: CALL NEWBF ;Set param for new RBN I/O MOV R0,DSKBF ;Set buffer address CALL MULWTC ;Write sector back to table (Compare) BCC REPLBN ;If CC, goto next step (12) 100$: .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG BEQ 110$ BIS #LFR.BR,L.RPFL(R0) 110$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 120$ BIS #LFR.TE,L.RPFL(R0) 120$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CALLR STEP15 ;Write failed, exit .DSABL LSB .SBTTL REPLBN - *BBR Step 12* Replace Bad LBN w/RBN; Write Saved Data in RBN ;**************************************************************************** ; STEP 12 ;**************************************************************************** ;* ;* 12. The process performs the REPLACE command followed by a ;* Write command (addressed to the bad block's LBN) to ;* cause the saved data to be REVECTORED to the RBN. ;* Note that REPLACE cmnd failure means the subsystem was ;* unable to write the replace code in the header of the ;* bad block or was unable to verify the success of that ;* operation. This indicates either a drive or media ;* problem. The consequence of this failure is that the ;* host cannot continue with the replacement process as ;* writes involving the LBN may fail. ;* ;* A. - REPLACE ACTIONS - ;* ;* a. The process uses the REPLACE command to insert ;* the replace code into the header of the bad ;* block to permanently identify the chosen ;* replacement block. If the REPLACE command ;* returns any status other than ST.SUC go to ;* REPLACE COMMAND FAILURE PROCEDURE below. ;* ;* b. If step 11 was entered from step 3 because the ;* Phase 2 flag was set, then go to step 12C ;* (the replacement was reentered after a failure ;* or loss of context). Otherwise the process ;* performs a read operation to verify that the ;* replacement succeeded. Note that the read will ;* cause the controller to revector to the RBN ;* and read the test pattern with Forced Error ;* that was written when the HDA was formatted. ;* ;* If the status code is Data Error with Forced ;* Error subcode, then go to WRITE and COMPARE ;* described below in step 12C. The LBN header ;* was modified & the Replace command succeeded. ;* If the returned status code is Data Error ;* with a nonzero subcode, then go to Step 9 to ;* rescan the RCT for another RBN. The current ;* new RBN will become the old RBN for this next ;* pass. This failure is to be reported to the ;* system error log. If the returned status code ;* is Success, then go to REPLACE COMMAND FAILURE ;* PROCEDURE in step 12B. ;* ;* ;* Rationale for the above decisions includes: ;* ;* o If the REPLACE failed before the replace code ;* was written to the LBN header, the state of the ;* bad block will be as if the replacement never ;* occurred. This is due to fact that the saved ;* data was put back into the block in step 8. ;* Notice the state is inconsistent because the RBN ;* descriptor in the RCT is modified, but the LBN ;* header is NOT. ;* ;* o If the REPLACE failed after the replace code was ;* inserted into the LBN header, or the sector was ;* corrupted such that the LBN header could not be ;* updated, then no attempt to write the saved data ;* to the bad block can be attempted because there ;* is no guarantee that the correct block will be ;* written. ;* ;* Note that the RBN descriptor may indicate a ;* REPLACEMENT has taken place even though the ;* saved data may not have been recorded in the ;* RBN. This creates the situation where any ;* ensuing read to the LBN will report a Forced ;* Error, since the initial RBN state is set to ;* Forced Error. The RBN Forced Error state can ;* only be changed by a write to the RBN. ;* ;* B. Replace Command Failure Procedure - ;* --------------------------------- ;* ;* To recover from a REPLACE COMMAND error the ;* following sequence must be performed. ;* ;* o Write the saved data (addressed to the bad ;* block's LBN) to update the replacement block or ;* overwrite the old saved data. ;* ;* o Assert the Data Safety Write Protect flag to ;* indicate the Replace Command failed. This is a ;* Software write lock which indicates the drive ;* associated with the failure must have a media ;* backup performed. ;* ;* o If a mechanism for recording operator messages, ;* such as a system console or operator log ;* subsystem is implemented, the replace command ;* failure should be logged using that mechanism. ;* In addition the error will be logged to the ;* error log. ;* ;* ;* An example of what is needed includes: ;* ;* Report on the System Console ;* ---------------------------- ;* ;* Replace Command Failure at LBN (LBN) ;* ;* Software Write Protecting Volume ;* ;* ;* Additional Procedures ;* --------------------- ;* ;* Perform a media BACKUP to recover ;* from the failure and pay particular ;* attention to any errors reported ;* during the media BACKUP. ;* ;* Use the error information reported ;* during the media BACKUP to ;* determine if any files were ;* corrupted. Restore, if possible, ;* any corrupted files. ;* ;* ;* o The state of the RCT at this point is: the LBN ;* address is saved in RCT sector 0, the saved data ;* is in RCT sector 1, and the RBN Descriptor(s) ;* have been updated to indicate a replacement ;* occurred. ;* ;* o Report replace failure to the error log. This ;* error cannot be logged if the disk containing ;* the error log has the replace failure. ;* ;* o Go to Step 17 to exit the replacement process. ;* ;* ;* C. WRITE and COMPARE - ;* ----------------- ;* ;* The process uses the standard WRITE command ;* (addressed to the bad block's LBN) to store the ;* saved data in the replacement block. If the saved ;* data is invalid (as indicated by the FE bit in the ;* memory-resident copy of RCT block 0), the Write ;* operation is performed with the forced error ;* modifier. The Write command succeeds if the ;* returned status code is Success. ;* ;* If the Write command succeeds, then perform a Read ;* of the block just written. The Read succeeds if the ;* returned status code is Success AND the BBR flag is ;* NOT set or if the status code is Data Error with ;* subcode Forced Error AND the saved data is invalid. ;* If the Read succeeds, then compare the data read ;* with the data written. The operation succeeds if ;* the data is the same. ;* ;* If the Write, Read, or compare operation fails, go ;* to step 9 to rescan the RCT for another RBN. The ;* current new RBN will become the old RBN for this ;* next pass. This failure is to be reported to the ;* system error log. ;* ;* ; ; Replace BAD LBN with RBN and write saved data in RBN ; .ENABL LSB REPLBN: .IF NE DU$BRD .ADDR #S12A,R0 .PRINT .ENDC MOV #OP.RPL,IOFUN ;Function code = replace MOV NRBNH,RPLBH ;Parameter 1 = RBN (Hi) MOV NRBNL,RPLBL ;Parameter 2 = RBN (Lo) MOV BLBNH,LBNH ;Parameter 3 = LBN (Hi) MOV BLBNL,LBNL ;Parameter 4 = LBN (Lo) CLR IOMOD ; Parameter 5 = RBN type (0=SEC, 1=PRI) BIS #BIT8,INTFLG ;Assume secondary CMP PLBNO,NLBNO ;Offts to RBNs in RCT bk =? BNE 10$ ;No, must be secondary CMP PLBNL,NLBNL ;Primary RBN same as new RBN? BNE 10$ ;No CMP PLBNH,NLBNH ;Is it? BNE 10$ ;No, it must be secondary MOV #MD.PRI,IOMOD ;Indicate primary BIC #BIT8,INTFLG 10$: MOV R2,-(SP) MOV #BBR.IO,R2 CALL GENIO ;Perform the replace MOV (SP)+,R2 ;*C* BCS 40$ ;Replace failed, goto 12b ; If STEP 11 was entered from STEP 3 because PHASE 2 flag was SET, ; goto 12C. 20$: BIT #BIT4,INTFLG ;Set ? BEQ 30$ ;No, continue BIC #BIT4,INTFLG ;Clear flag CALLR 70$ ;Yes goto 12C ; Now perform a read operation to verify the replacement worked. ; Setup parameters for DSKIO. ; Read RBN - it should have a forced error from manufacturer 30$: MOV #OP.RD,IOFUN ;Get read modifier MOV #RCTBF,DSKBF ;Get buffer $REL .-4 RCTBF UMY CLR IOMOD ;Get modifier MOV R2,-(SP) MOV #BBR.IO,R2 CALL DSKIO ;Go read it MOV (SP)+,R2 BCC 40$ ;Success, its an error !!! CMP IOST+2,#ST.DAT ;Data error w/ forced error ? BEQ 70$ ;Replace worked !!! CALLR SCAN1 ;Error, branch ;************************ END OF STEP 12 A ********************************** ;**************************************************************************** ; STEP 12B ;**************************************************************************** ; The DATA SAFETY WRITE PROTECT is performed by TROUBLE 40$: .IF NE DU$BRD .ADDR #S12B,R0 .PRINT .ENDC .IF EQ DU$ERL-2 MOV #BBRPAK,R0 ;send the BBR attempted packet $REL .-2 BBRPAK UMY MOV #BB.FAI,L.EVNT(R0) BISB #LF.RCT,L.FLGS(R0) BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIS #LFR.RF,L.RPFL(R0) ;set reformat flag because replace BIT #RF.BR,SEC0+RCTFLG ;failed BEQ 50$ BIS #LFR.BR,L.RPFL(R0) 50$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 60$ BIS #LFR.TE,L.RPFL(R0) 60$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 MOV BLBNL,LBNL ;Address to write MOV BLBNH,LBNH ;the data to CALL WTSAV ;Go write saved data -> BLBN CALLR TROUBLE ;******************* END OF STEP 12 B *************************************** ;**************************************************************************** ; STEP 12C ;**************************************************************************** ;Perform the write to the LBN 70$: .IF NE DU$BRD .ADDR #S12C,R0 .PRINT .ENDC MOV #OP.WR,IOFUN ;Set function to write MOV #LBN,DSKBF ;LBN is what to write $REL .-4 LBN UMY ;Up up and away CLR IOMOD ;Assume not FE BIC #BIT2,INTFLG BIT #RF.FE,SEC0+RCTFLG ;Is FE set ? BEQ 80$ ;No, branch MOV #MD.ERR,IOMOD ;Set the FE bit for write BIS #BIT2,INTFLG 80$: MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Perform write MOV (SP)+,R2 BCS 120$ ;Write failed, goto step 9 .BR 90$ 90$: MOV #OP.RD,IOFUN ;Say read MOV #RCTB1,DSKBF ;Read to RCTB1 $REL .-4 RCTB1 UMY ;Up up and away ; NOTE: Read to RCTB1 because we might have to compare write/read data ; if this read works. MOV #MD.CMP,IOMOD ;Say compare modifier MOV R2,-(SP) MOV #BBR.IO!IGN.IO,R2 CALL DSKIO ;Perform read MOV (SP)+,R2 .ASSUME ST.SUC EQ 0 TST IOST+2 ;Success ? BNE 100$ ;No, go see if data error ; *and* save data invalid TST IOST ;BBR flag set ? No=success BPL 130$ ;No, go do compare ; Hit here, read didn't work. Goto STEP 9 BR 120$ ;Goto step 9 ; See if DATA ERROR and saved data is invalid 100$: CMP #ST.DAT,IOST+2 ;Data error w/ F.E. ? BNE 120$ ;No, read didn't work. ;Goto step 9 110$: BIT #RF.FE,SEC0+RCTFLG ;Data invalid ? BNE 130$ ;Yes, branch .BR 120$ 120$: CALLR SCAN1 ;Goto step 9 ; Read worked. Now compare what we have written with what we have read. 130$: BIC #BIT6,INTFLG ;Assume won't match PUSH ;Save R0-R2 MOV #BLKLEN,R0 ;R0=counter # words MOV #LBN,R1 ;R1 ->write's buffer $REL .-2 LBN UMY ;Up up and away MOV #RCTB1,R2 ;R2 ->read's buffer $REL .-2 RCTB1 UMY ;Up up and away 140$: CMP (R1)+,(R2)+ ;Compare data BNE 150$ ;Not equal SOB R0,140$ ;branch if more words to compare BIS #BIT6,INTFLG ;Set flag for success 150$: POP ;Restore registers BIT #BIT6,INTFLG ;Did operation succeed ? BNE 160$ ;Yes, branch CALLR SCAN1 ;No, goto step 9 ; Operation Succeeded !!! 160$: BIC #BIT7,INTFLG .BR RESDAT ;Goto next step (13) ;*************************** END OF STEP 12C ******************************* .DSABL LSB .SBTTL RESDAT - *BBR Step 13* Update Sect 0 of RCT Copies: Blk Replace Done ;**************************************************************************** ; STEP 13 ;**************************************************************************** ;* 13. The process updates sector 0 of the RCT copies to ;* indicate that it is no longer in the middle of replacing ;* a bad block. The RCT must be updated without reading ;* sector 0, instead using the copy of sector 0 last read ;* from or written to the RCT. ;* ;* If the RCT cannot be updated, report the error to the ;* error log and go to step 17. RESDAT: .IF NE DU$BRD .ADDR #S13,R0 .PRINT .ENDC CALL FFINSZ ;No longer doing replacing BCC STEP14 ;No error, continue .IF EQ DU$ERL-2 CALL DTEMOD MOV #BBRPAK,R0 ;log a bad block replacement attempt $REL .-2 BBRPAK UMY ;error log packet MOV #BB.RCI,L.EVNT(R0) ;event code = inconsistent RCT BISB #LF.RCT,L.FLGS(R0) ;error during replacement BIS #LFR.RI,L.RPFL(R0) ;inconsistent RCT BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG BEQ 10$ BIS #LFR.BR,L.RPFL(R0) 10$: BIT #BIT7,INTFLG BEQ 20$ BIS #LFR.RP,L.RPFL(R0) 20$: BIT #BIT8,INTFLG ;non primary replacement? BEQ 30$ BIS #LFR.TE,L.RPFL(R0) 30$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 BR STEP17 ;Goto step17 .SBTTL STEPS 14 - 18 *BBR Exit Points* ;**************************************************************************** ; STEPS 14 - 15 - 16 - 17 - 18 ! ;**************************************************************************** ;* 14. The process releases the lock it acquired in step 2 and ;* exits. ;* ;* 15. The process restores the RCT to indicate that: ;* ;* o the new RBN descriptor is unallocated and usable, ;* ;* o and the old RBN descriptor indicates the bad block ;* is either NOT replaced or IS revectored to the old ;* RBN, whichever was it's original status. ;* ;* The RCT must be updated without reading any blocks from ;* it, instead using the copies of the relevant blocks that ;* were read from the RCT in step 11. Any errors are ;* reported to the error log but otherwise ignored. ;* ;* 16. The process uses the standard WRITE command (addressed ;* to the bad block's LBN) to restore the saved data. The ;* write is performed with the forced error modifier if and ;* only if the saved data is invalid (FE bit set in RCT ;* block 0). Any errors are reported to the error log but ;* otherwise ignored. ;* ;* 17. The process updates sector 0 of the RCT copies to ;* indicate that it is no longer in the middle of replacing ;* a bad block. The RCT must be updated without reading ;* sector 0, instead using the copy of sector 0 last read ;* from or written to the RCT. Any errors are reported to ;* the error log but otherwise ignored. ;* ;* 18. The process releases the lock it acquired in step 2. If ;* a controller is performing the bad block replacement, it ;* reports an MSCP Media Format Error as a serious ;* exception to the host. If a host is performing the bad ;* block replacement, it takes whatever host dependent ;* action is appropriate for a failed bad block replacement ;* operation. ;* .ENABL LSB STEP14: .IF NE DU$BRD .ADDR #S14,R0 .PRINT .ENDC .IF EQ DU$ERL-2 MOV #BBRPAK,R0 $REL .-2 BBRPAK UMY BISB #LF.SUC,L.FLGS(R0) ;operation was successful BIT #BIT7,INTFLG ;if block turned out to be OK, then BEQ 10$ ;use block-not-replaced event code MOV #BB.OK,L.EVNT(R0) BR 20$ 10$: MOV #BB.SUC,L.EVNT(R0) ;block was successfully replaced BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag 20$: BIT #BIT2,INTFLG BEQ 30$ BIS #LFR.FE,L.RPFL(R0) 30$: BIT #RF.BR,SEC0+RCTFLG BEQ 40$ BIS #LFR.BR,L.RPFL(R0) 40$: MOV NRBNL,L.NRBN(R0) MOV NRBNH,L.NRBN+2(R0) MOV ORBNL,L.ORBN(R0) MOV ORBNH,L.ORBN+2(R0) BIT #BIT8,INTFLG ;non primary replacement? BEQ 50$ BIS #LFR.TE,L.RPFL(R0) 50$: CALL DO.BBR .ENDC ;EQ DU$ERL-2 CLR USTAT ;Clear Update flag CLR INTFLG ;Clear flag ;DO OP. OVER AGAIN TO MAKE RETURNED ;STATUS CODES ARE CORRECT MOV #BEGIN,-(SP) $REL .-2 BEGIN UMX JMP UMYRET STEP15: .IF NE DU$BRD .addr #s15,r0 .print .ENDC ; NE DU$BRD CALL UNALL ;Restore RCT BCC STEP16 ;Ok. Continue .IF EQ DU$ERL-2 CALL DTEMOD .ENDC ;EQ DU$ERL-2 STEP16: .IF NE DU$BRD .ADDR #S16,R0 .PRINT .ENDC ;NE DU$BRD CALL WTSAV ;Restore saved data BCC STEP17 ;Ok. Continue ;error logging taken care of by the MUL(R/W) routines STEP17: CALL FFINSZ ;Update sector 0 BCC STEP18 ;Ok. Continue .IF EQ DU$ERL-2 CALL DTEMOD .ENDC ;EQ DU$ERL-2 STEP18: .ENABL LSB BIT #US.OLP,USTAT ;Bringing unit online ? BNE 60$ ;Yes, no error DEATH: CLR USTAT ;Update flag CLR INTFLG ;Clear flag ;DO OP. OVER AGAIN TO MAKE RETURNED ;STATUS CODES ARE CORRECT 60$: MOV #BEGIN,-(SP) ;Go Merge $REL .-2 BEGIN UMX JMP UMYRET .DSABL LSB .SBTTL BBRSUB - *BBR Subroutines* .SBTTL DSKIO - *BBR* Initiate Disk I/O Routine ;+ ; DSKIO - Issue Single Block I/O tO Disk ; GENIO - Issue I/O to Disk ; ; Subroutine to issue disk I/O ; ; The following function and status codes are used for I/O: ; ; OP.WR/OP.RD Write/read block ; ; MD.CMP!MD.ERR Write Deleted Data (OP.WR with "Force-error" Modifier) ; ; OP.RPL Replace ; ; Input: ; LBNL = LBN, Low order ; LBNH = LBN, High order ; DSKBF = Buffer Address (offset from beg of PAR1) ; IOFUN = Function Code ; IOMOD = Command Modifier ; RPLBL = Bad Block Low ; RPLBH = Bad Block High ; XFRSZ = Transfer Size ; R2 (lowest 5 bits) = "type bits" for the command reference number ; other bits must be 0 ; ; Output: ; C = 0 if success ; C = 1 if failure ;- .ENABL LSB DSKIO:: ;Issue single block QIO to disk MOV #2*BLKLEN,XFRSZ ;Set transfer size (bytes) GENIO: PUSH .IF NE MMG$T ;get the physical address of the start of the ;UMY region in extended memory MOV R2,-(SP) ;protect the CRF "type bits" MOV #UMRH,R2 $REL .-2 UMRH UMX .YP1EX P1UMX MOV (R2)+,R5 ;R5 = physical high addr MOV @R2,R4 ;R4 = physical low address .YP1EN MOV (SP)+,R2 ADD #UMXSIZ+PADLEN,R4 ADC R5 .IFF ;NE MMG$T CLR R4 ;affect of UMY region on the physical CLR R5 ;address is zero in SJ/FB .IFTF ;NE MMG$T MOV DSKBF,R0 ;R0 = virtual address of buffer .IFT ;NE MMG$T ;ACTION - will this SUB work in loaded, non-system handler, XM DU? SUB #BEGREL,R0 ;R0 = offset from physical address ; !!! BEGREL is conditionally defined ; as per MMG$T !!! .IFTF ;NE MMG$T ADD R0,R4 ;Add the offset... .IFT ;NE MMG$T ADC R5 ;Into the 22-bit quantity! .ENDC ;NE MMG$T MOV R4,DPHYSL ;Store phys. addr. of buff, low order MOV R5,DPHYSH ;Store phys. addr. of buff, high order MOV #GETCBF,R1 $REL .-2 GETCBF UMX MOV #.UTTAB,R3 $REL .-2 .UTTAB UMX .YP1EX P1UMX JSR R5,@R1 ;call GETCBF down in the UMX psect .WORD OP.NIL ;these are args to GETCBF for the .WORD BBR.IO ;packet it will make BIS R2,P.CRF(R4) ;Set various "type" bits in C. Ref. MOV @(R3),P.UNIT(R4) ;Store the MSCP unit number in the .YP1EN ;command packet .IF NE MMG$T ;NOW USE BLKMOV to copy from the command buffer to ;to the command buffer used by the BBR algorithm MOV R4,-(SP) ;R4 -> CBUFF0 (PAR1 = UMX) MOV P1UMX,R1 ;R1 = source PAR1 bias MOV R4,R2 ;R2 = source PAR1 offset MOV @#KISAR1,R3 ;R3 = destination PAR1 bias MOV #BBRCOM,R4 ;R4 = destination of PAR1 offset $REL .-2 BBRCOM UMY MOV #P.CSIZ/2,R5 ;R5 = word count MOV Y$P1EX,R0 CALL $BLMPT(R0) ;call BLKMOV MOV #BBRCOM,R2 $REL .-2 BBRCOM UMY .IFF ;NE MMG$T MOV R4,R2 .ENDC ;NE MMG$T MOV IOFUN,P.OPCD(R2) ;Store the opcode MOV IOMOD,P.MOD(R2) ;Store the modifiers CMP #OP.RPL,P.OPCD(R2) ;Is this a replace ? BEQ 10$ ;Yes, branch MOV LBNH,P.LBN+2(R2) ;Save high order LBN in packet MOV LBNL,P.LBN(R2) ;Save low order LBN MOV XFRSZ,P.BCNT(R2) ;Save byte count1; MOV DPHYSH,P.BUFF+2(R2) ;Store buffer high MOV DPHYSL,P.BUFF(R2) ;Store buffer low BR 20$ ;Skip next 10$: MOV RPLBH,P.RBN+2(R2) ;Put high replacement block in packet MOV RPLBL,P.RBN(R2) ;Put low replacement block in packet MOV LBNH,P.LBN+2(R2) ;Save high order LBN in packet MOV LBNL,P.LBN(R2) ;Save low order LBN 20$: .IF NE MMG$T MOV @#KISAR1,R1 MOV P1UMX,R3 MOV (SP)+,R4 MOV #P.CSIZ/2,R5 MOV Y$P1EX,R0 CALL $BLMPT(R0) .ENDC ;NE MMG$T CALL SCONTX ;Go save context - start poll ;+ ; Entry Point - From Restore of context. ; ; MAP - PAR1 (DUADDR) ; R1 = BEGREL, R2 = ACTBBR ;- MOV #RESPON,R4 $REL .-2 RESPON UMY MOV P.STS(R4),-(SP) ;Save status on stack (AS A WORD) MOVB P.FLGS(R4),-(SP) ;Save flags byte on stack BIT #MD.ERR,IOMOD ;Was forced error used as a mod ? BEQ 30$ ;NO, skip next BIC #ST.DAT,2(SP) ;Clear the forced error flag! 30$: CLC ;Assume success MOVB (SP)+,IOST+1 ;*C* Save flags in highbyte of IOST MOV (SP),IOST+2 ;*C* Save the Full P.STS in 2nd wrd BIC #^C,(SP) ;*C* Save only major code in lowbyt MOVB (SP)+,IOST ;*C* $IOST = Major code+flags BEQ 50$ ;*C* and if both=0, then success 40$: SEC ;Indicate I/O failed 50$: MOV #0,IOMOD ;*C* Clear IOMOD and preserve carry POP ;*C* RETURN .DSABL LSB .SBTTL GETMES - *BBR* get end message of a BBR I/O out of the Ring's buffer ;+ ; GETMES - move the synchronous end message of a BBR I/O out the UQSSP ; Ring's buffer, into the buffer RESPON. ; After this routine, GETNEXT can be called on the port ;- .ENABL LSB GETMES: PUSH MOV #RESPON,R4 ;R4 = destination buffer address $REL .-2 RESPON UMY MOV ACTBBR,R2 MOV BB.BUF(R2),R2 ;R2 = source buffer addres MOV #/2,R5 SUB #ENVLEN,R4 SUB #ENVLEN,R2 .IF NE MMG$T ;use BLKMOV to copy end message up to RESPON MOV P1UMX,R1 MOV @#KISAR1,R3 MOV Y$P1EX,R0 CALL $BLMPT(R0) .IFF ;NE MMG$T 10$: MOV (R2)+,(R4)+ SOB R5,10$ .ENDC ;NE MMG$T POP RETURN .DSABL LSB .SBTTL RETMES - *BBR* put end message of original I/O back in real buffer ;+ ; RETMES - put end message of original I/O back in real buffer ;- .ENABL LSB RETMES: PUSH MOV #RESPON,R2 ;R2 = source buffer address $REL .-2 RESPON UMY MOV ACTBBR,R4 MOV BB.BUF(R4),R4 ;R4 = destination buffer address MOV #/2,R5 SUB #ENVLEN,R4 SUB #ENVLEN,R2 .IF NE MMG$T ;use BLKMOV MOV P1UMX,R3 MOV @#KISAR1,R1 MOV Y$P1EX,R0 CALL $BLMPT(R0) .IFF ;NE MMG$T 10$: MOV (R2)+,(R4)+ SOB R5,10$ .ENDC ;NE MMG$T POP RETURN .DSABL LSB .IF EQ DU$ERL-2 .SBTTL INITPK - *BBR* initialize the datagrams created by the BBR algorithm ;+ ; INITPK - initialize the datagrams created by the BBR algorithm ; Assumes that the end message that it talks to is a GET UNIT ; STATUS end message ; .ENABL LSB INITPK: PUSH MOV #BBRPAK,R0 ;R0 -> bad block replacement attempt $REL .-2 BBRPAK UMY ;error log datagram MOV #DTEPAK,R1 ;R1 -> disk transfer error log $REL .-2 DTEPAK UMY ;datagram MOV ACTBBR,R5 MOV BB.CRF(R5),(R0)+ ;copy the command reference number, MOV BB.CRF+2(R5),(R0)+ ;this is the CRF of the packet that MOV BB.CRF(R5),(R1)+ ;reported the bad block, MOV BB.CRF+2(R5),(R1)+ MOV BB.BUF(R5),R5 .IF NE MMG$T ;copy the GUS end message up to RESPON PUSH MOV R5,R2 ;R4 = source buffer address MOV #RESPON,R4 ;R4 = destination buffer address $REL .-2 RESPON UMY MOV @#KISAR1,R3 MOV P1UMX,R1 MOV #P.MSIZ/2,R5 MOV Y$P1EX,R0 CALL $BLMPT(R0) ;call BLKMOV POP MOV #RESPON,R5 $REL .-2 RESPON UMY .ENDC ;NE MMG$T ADD #P.UNIT,R5 MOV #2,R2 10$: MOV @R5,(R0)+ ;copy the unit number and sequence number MOV (R5)+,(R1)+ SOB R2,10$ ;set the bit in the command reference number that indicates ;that is packet is relevent to a host initiated BBR BIS #BBR.IO,L.CRF-L.FMT(R0) BIS #BBR.IO,L.CRF-L.FMT(R1) MOV #FM.RPL,(R0)+ ;insert format fields into the packets MOV #FM.DSK,(R1)+ ;and zero the P.FLGS fields CLR (R0)+ ;zero the event code fields CLR (R1)+ MOV ACTBBR,R3 MOV BB.POR(R3),R3 ;R3 -> port control table entry .IF NE MMG$T ;copy the port control table entry for this port into PCBUF PUSH MOV #PC.ESZ/2,R5 ;word count MOV #PCBUF,R4 ;destination addr. $REL .-2 PCBUF UMY MOV R3,R2 ;souce addr. MOV @#KISAR1,R3 ;destination PAR1 MOV P1UMX,R1 ;source PAR1 MOV Y$P1EX,R0 CALL $BLMPT(R0) POP ;call BLKMOV MOV #PCBUF,R3 $REL .-2 PCBUF UMY .ENDC ;NE MMG$T ADD #PC.ID,R3 ;R3 -> controller id. and version ;numbers - this is 10. bytes long MOV #5,R4 ;now, copy the controller identifier 20$: MOV @R3,(R0)+ ;and controller c(s/h)vsrn into MOV (R3)+,(R1)+ ;the datagram packets SOB R4,20$ .ASSUME PC.VOL EQ PC.ID+10. ;R3 -> volume serial number of unit MOV @R3, L.VSER-L.MLUN(R0);copy volume serial number, low MOV (R3)+,L.VSER-L.MLUN(R1);order, into packets MOV (R3), L.VSER-L.MLUN+2(R0);copy volume serial number, high MOV (R3)+,L.VSER-L.MLUN+2(R1);order, into packets MOV P.MLUN-P.OPCD(R5),(R0)+ MOV P.MLUN-P.OPCD(R5),(R1)+ ADD #P.UNTI-P.OPCD,R5 ;advance R5 to -> unit identifier ;field of the GUS end message MOV #4,R4 ;now, copy the unit identifier 30$: MOV @R5,(R0)+ ;into the datagram packets - MOV (R5)+,(R1)+ ;this is 8. bytes SOB R4,30$ MOV P.USVR-P.MEDI(R5),(R0)+ ;move the unit software and hardware MOV P.USVR-P.MEDI(R5),(R1)+ ;version numbers into the packets CLR (R0)+ ;zero the replace flags CLR (R1)+ ;zero the retry/level word, a ;device dependent field ADD #4,R0 ;Skip over the Volume Serial Number ADD #4,R1 ;which we have already filled MOV #6,R4 ;zero the Bad LBN, Old RBN, and 40$: CLR (R0)+ ;New LBN fields. These fields will ;be filled in appropriately by the SOB R4,40$ ;algorithm ;R0 now points to the cause field of the BBR packet. ;copy the status/event code from the end message that ;originally invoked the bad block replacement algorithm ;into the Cause field of the BBR attempted packet - the code ;was stored in ORGCOD by BBFND when we saw originally ;detected a bad block. ;in step 3. MOV ORGCOD,@R0 CLR ORGCOD POP RETURN .DSABL LSB .SBTTL DTEMOD *BBR* Send a possibly modified DTE packet to error logger ;+ ; DTEMOD Send a possibly modified DTE packet to error logger ; ;- .ENABL LSB DTEMOD: MOV #DTEPAK,R0 $REL .-2 DTEPAK UMY MOV ACTBBR,R2 MOV BB.COD(R2),L.EVNT(R0) ;stuff the status event code MOV BB.COD(R2),-(SP) BIC #^c,@SP CMP (SP)+,#ST.DAT ;If it is a data error event code, BNE 10$ ;then change it to a media format SUB #3,L.EVNT(R0) ;error .ASSUME ST.MFI+3 EQ ST.DAT 10$: BISB #LF.RCT,L.FLGS(R0) ;say error during replacement CALL DO.DTE RETURN .DSABL LSB .SBTTL DO.BBR/DO.DTE - *BBR* send an error log datagram to the error logger ;+ ; DO.BBR - send the BBR Attempted datagram to the error logger ; DO.DTE - send a Disk Transfer Error datagram to the error logger ; ; any Disk Transfer Error datagram that the BBR algorithm sends to the ; error logger will have the Error During Replacement flag set - so DO.DTE ; sets it. Other than that, there is no difference between DO.DTE/DO.BBR. ; Fields within the packets (pointed to by R0) have stuffed appropriately ; by the caller. ; ;- DO.DTE: BISB #LF.RCT,L.FLGS(R0) MOV BLBNL,L.HDCD(R0) ;Disk Transfer Error packet has MOV BLBNH,L.HDCD+2(R0) ;LBN number in header code field DO.BBR: PUSH .IF NE MMG$T ;move the datagram down to low memory MOV R0,-(SP) MOV #P.MSIZ/2,R5 ;R5 = word count MOV #ERLBUF-ENVLEN,R4 ;R4 = destination address $REL .-2 ERLBUF-ENVLEN UYR MOV R4,R3 ;make PAR1 offset/bias pair for $BLMPT BIC #^C<77>,R4 ADD #BEGREL,R4 ASH #-6,R3 BIC #^C<1777>,R3 MOV R0,R2 SUB #4,R2 ;R2 = source address MOV @#KISAR1,R1 ;R1 = source PAR1 bias MOV Y$P1EX,R0 CALL $BLMPT(R0) ;call BLKMOV MOV (SP)+,R0 .ENDC ;NE MMG$T MOV ACTBBR,R3 ;set R3 for up call to ERRLOG MOV BB.POR(R3),R3 .IF EQ MMG$T MOV R0,R5 ;set up R5 for call to errlog .IFF ;EQ MMG$T MOV #ERLBUF-ENVLEN,R5 $REL .-2 ERLBUF-ENVLEN UYR .ENDC ;EQ MMG$T ;call ERLLOG, which is in the other PAR1 space MOV #ERLLOG,R1 $REL .-2 ERLLOG UMX .YP1EX P1UMX CALL @R1 .YP1EN POP RETURN .ENDC ;EQ DU$ERL-2 .SBTTL FFINSZ - *BBR* Update Table Sector 0 to Show RCT is Finished ;+ ; FFINSZ - Update Table Sector 0 to Show RCT is Finished ; ; This routine will clear the table flag words and write SECTOR 0 back ; out to the Replacement Control Table on disk. ; ; Output: ; Table SECTOR 0 is updated. ; C = 0 if successful ; C = 1 if failure ;- FFINSZ::MOV #SEC0+RCTFLG,R3 ;R3 -> RCTFLG $REL .-2 SEC0+RCTFLG UMY ;$REL understands XM vs. FB/SJ MOV #8.,R4 ;R4 = number of words to clr 10$: CLR (R3)+ ;Clear location SOB R4,10$ ;Branch if we are not finished CALLR MULWTB ;Setup & Write sector 0 of table .SBTTL HASH - *BBR* Primary Replacement Control Table HASH Algorithm ;+ ; HASH - Primary Replacement Control Table HASH Algorithm ; ; This algorithm produces the block number within the first copy of ; the table which contains the primary RBN descriptor for the bad LBN, ; and also produces the offset within the block to that RBN descriptor. ; ; BLOCK NUMBER = QUO((QUO(LBN/TRKSZ)*RBNPT)/128)+(MXLBN)+2 ; ; OFFSET = REM((QUO(LBN/TRKSZ)*RBNPT)/128) ; Input: ; BLBNL = Bad LBN to be replaced, Low order ; BLBNH = Bad LBN to be replaced, High order ; TRKSZ = Number of LBNs per track ; RBNPT = Number of RBNs per track ; MXLBN = Maximum user addressable LBNs ; ; Output: ; PLBNL = Blk number in first copy of table for primary RBN, Lo order ; PLBNH = Blk number in first copy of table for primary RBN, Hi order ; PLBNO = Offset within above block to the primary RBN descriptor ;- .ENABL LSB HASH:: ;Primary hash algorithm PUSH ;Save registers MOV R1,R4 ;R4 -> bias MOV R2,R5 ;R5 -> current table ACTBBR MOV BB.TRK(R5),R0 ;R0 = LBNs per track = divisor MOV BLBNH,R1 ;R1 = bad LBN = dividend, high order MOV BLBNL,R2 ;R2 = bad LBN = dividend, low order CALL DDIV ;Perform double precision divide ; R1 = QUO(LBN/TRKSZ), high order ; R2 = QUO(LBN/TRKSZ), low order MOV R2,R3 ;R3 = low order multiplicand MOV R1,R2 ;R2 = high order multiplicand CLR R0 ;Get ready for the BISB BISB BB.RBN(R5),R0 ;R0 = RBNs per track = multiplier CALL DMUL ;Perform double precision multiply ; R0 = (QUO(LBN/TRKSZ)*RBNPT), hi ord ; R1 = (QUO(LBN/TRKSZ)*RBNPT), lo ord MOV R1,R2 ;R2 = low order dividend MOV R0,R1 ;R1 = high order dividend MOV #128.,R0 ;R0 = 128. = divisor CALL DDIV ;Perform double precision divide ; R0 = REM((QUO(LBN/TRKSZ)*RBNPT)/128) ; R1 = QUO((QUO(LBN/TRKSZ)*RBNPT)/128), hi ; R2 = QUO((QUO(LBN/TRKSZ)*RBNPT)/128), lo ADD BB.MAX(R5),R2 ;Add max user addressable LBN, low ord ADC R1 ;... and carry ADD BB.MAX+2(R5),R1 ;Add max user addressable LBN, hi ord ADD #2,R2 ;Add 2 ADC R1 ;... and carry MOV R2,PLBNL ;PLBNL = blk number in table, low ord MOV R1,PLBNH ;PLBNH = blk number in table, high ord ASL R0 ;Not saving long words, we'll get the ASL R0 ;byte offset into blk, and save that MOV R0,PLBNO ;PLBNO = offset within block POP ;Restore registers RETURN ; .DSABL LSB .SBTTL TROUBLE - *BBR* software write protect Unit, print Disaster message ;+ ;this code handles BBR disasters, namely: ; ; 1) REPLACE command failed ; 2) Multiread or Multiwrite of RCT failed ; ;the software write-protection of the disk with a bad RCT or bad replacement ;is performed via the Set Unit Characteristics command. A disaster message ;that the disk should be backed up immediately is displayed on the console ;terminal. In order to get the message to the console a SYNCH to job 0 is ;performed .ENABL LSB TROUBLE:;we would like to do the following but because we are in the UMY ;psect, we cannot; this is because the MSCP and the DO macros do ;not work in the UMY psect, only in the UMX psect - ;MSCP OP.SUC,BBR.IO ;Set unit chars ;MOV R4,R2 ;Save pointer to comm buff ??????? ;MOV #IOP.NR,INTEOP ;Internal op w/out recovery ;DO SETSWP,THEN,SINK ;Software write protect ;And then notify user ;- insead we do the following - MOV #DOSINK,-(SP) $REL .-2 DOSINK UMX JMP UMYRET SINK: CLR R1 ;Clear out flag .IF EQ MMG$T .BR LOWONE .IFF ;EQ MMG$T .YP1EX P1LW2 ;LOWONE, which is where we do the JMP @#LOWONE ;.SYNCH, is in low memory so we must $REL .-2 LOWONE UYR ;use P1EXT to jump to it .YP1EN .ENDC ;EQ MMG$T ;**************************************************************************** UMPSECT ;go back to low memory for the .SYNCH ;this should restore us to UMLCODE ;**************************************************************************** LOWONE: .IF NE MMG$T MOV 2(SP),SP ;clean up after P1EXT CMP (SP)+,(SP)+ MOV (SP)+,R0 .ENDC ;NE MMG$T ; Want to clear job number to force .PRINTS to BG'S terminal. CLR SYNBLK+2 ;Clear job number ; .SYNCH #SYNBLK ;Go SYNCH ; ;ACTION! .Synch takes a second argument, PIC. .IF NB PIC, the macro generates ; position independent code. DO WE, therefore, need to "simulate" this ; .Synch? ; ; Simulate a .SYNCH request .ADDR #SYNBLK,R4 MOV @#$SYPTR,R5 JSR R5,@$SYNCH(R5) NOP ;No special branch on error, we simply ;will not do any I/O if the Synch ;failed MOV R0,R1 ;Save return status JSR R2,SAVE25 ;Go save reg .IF NE MMG$T MOV #G2HION,-(SP) ;transfer to G2HION, which will take $REL .-2 G2HION UMX ;us to HIONE MOV @#KISAR1,-(SP) .P1EXT P1HIGH ;Map to high CALLR @#HCALL $REL .-2 HCALL UMX .P1END .IFF ;NE MMG$T CALLR G2HION .ENDC ;NE MMG$T SYNBLK: .WORD 0,0,0,0,2,-1,0 ;.SYNCH block SAVE25: MOV R3,-(SP) ;Save R3. R2 saved by JSR R2 MOV R4,-(SP) ;Save R4 MOV R5,-(SP) ;Save R5 ;the following call will take us to STEP18; STEP18 will jump to ;BEGIN; BEGIN will do a .DRFIN, and the .DRFIN code will return ;us back to here. CALL @R2 ;Call caller back MOV (SP)+,R5 ;Restore R5 MOV (SP)+,R4 ;Restore R4 MOV (SP)+,R3 ;Restore R3 MOV (SP)+,R2 ;Restore R2 RETURN ;Return to monitor ;**************************************************************************** ;this should do a .save of UMLCODE UMYPSECT ;go back to high memory after ;the SYNCH ;*************************************************************************** HIONE: CMP #2,R1 ;Synch a success ? BNE 50$ ;No, don't do I/O MOV #CRLF,R0 ;R0 -> CRLF $REL .-2 CRLF UMY ;clean out display with a CR and LF .PRINT ;Carriage Return Line-Feed MOV #MSG1,R0 ;R0 -> message to print $REL .-2 MSG1 UMY ;Up up and away .PRINT ;Say replace failed MOV #CRLF,R0 ;R0 -> CRLF $REL .-2 CRLF UMY ;Up up and away .PRINT ;Carriage Return Line-Feed MOV #MSG2,R0 ;R0 -> Soft WP Volume message $REL .-2 MSG2 UMY ;Up up and away .PRINT ;Say Software W.P. Volume MOV #CRLF,R0 ;R0 -> CRLF $REL .-2 CRLF UMY ;Up up and away .PRINT ;Carriage Return Line-Feed 50$: CALLR DEATH ;take hard error exit .DSABLE LSB .SBTTL MULRDx - *BBR* Perform Multiple-Copy Read from Disk ;+ ; MULRD Perform Multiple-copy Read from Disk ; MULRDC Set Compare Modifier, then do MULRD ; MULRDB Setup DSKBF, LBNL, LBNH, then do MULRD ; ; These routines are used to read the multiple Replacement Control Table ; copies on the disk. Each copy is read until one succeeds. These routines ; are considered a success if any copy is read successfully. ; ; Input: ; SEC0 = (MULRDB only) Used to setup DSKBF (below) ; BB.MAX(R2) = (MULRDB only) Used to setup LBNL (below) ;BB.MAX+2(R2) = (MULRDB only) Used to setup LBNL (below) ; ; LBNL = LBN of sector in first copy, Low order ; LBNH = LBN of sector in first copy, High ORDER ; DSKBF = Buffer Address ; RCTSZ = Table Size ; RCTCP = Number of Table Copies ; ; Output: ; C = 0 if success ; C = 1 if failure ;- .ENABL LSB MULRDB::CALL MULRWB ;set up sector zero addresses BR MULRD ;Don't muck w/compare modifier MULRDC::MOV #MD.CMP,IOMOD ;Say compare modifier MULRD:: ;Perform multiple-copy read from disk PUSH ;Save input LBN as it gets changed PUSH ;Save registers MOV ACTBBR,R2 ;R2 -> BBR info MOV BB.RCT(R2),R4 ;Get table size CLR R0 ;Get ready for the BISB BISB BB.COP(R2),R0 ;Set number of copies MOV #OP.RD,IOFUN ;Set function code to read physical 10$: MOV #BBR.IO,R2 CMP R0,#1 ;if this is the last copy to try BEQ 20$ ;then we do not set the ignore bit, BIS #IGN.IO,R2 ;so that errors will goto the logger 20$: MOV IOMOD,-(SP) ;Save if have to call DSKIO > 1 time CALL DSKIO ;Perform the read MOV (SP)+,IOMOD ;*C* Restore modifier flags BCC 30$ ;If CC success ADD R4,LBNL ;Get address of sector in next copy ADC LBNH ;... SOB R0,10$ ;branch if not done SEC ;Otherwise, indicate failure BR 40$ 30$: .IF EQ DU$ERL-2 TST R0 ;If R0=0, then we succeeded on the BNE 40$ ;LAST copy of the RCT, which must be MOV #DTEPAK,R0 ;reported as NO-MULTICOPY-PROTECTION $REL .-2 DTEPAK UMY ;error. MOV #MF.NMP,L.EVNT(R0) CALL DO.DTE CLC .ENDC ;EQ DU$ERL-2 40$: POP ;*C* Restore registers POP ;*C* Restore LBN at entry RETURN .DSABL .SBTTL MULWTx - *BBR* Perform Multiple-Copy Write to Disk ;+ ; MULWT - Perform Multiple-copy Write to Disk ; MULWTC - Set Compare Modifier, then do MULWT ; MULWTB - Setup DSKBF, LBNL, LBNH then do MULWTC ; ; These routines are used to write the multiple Replacement Control Table ; copies on the disk. An attempt is made to write all copies. These ; routines are considered a success if any copy is written successfully. ; ; Input: ; SEC0 = (MULWTB only) Used to set DSKBF (below) ; BB.MAX(R2)= (MULWTB only) Used to set LBNL (below) ; BB.MAX+2(R2)= (MULWTB only) Used to set LBNH (below) ; ; LBNL = LBN of sector in first copy, Low order ; LBNH = LBN of sector in first copy, High order ; DSKBF = Buffer Address ; RCTSZ = Table Size ; RCTCP = Number of Table Copies ; ; Output: ; C = 0 if Success ; C = 1 if Failure ;- .ENABL LSB MULWTB::CALL MULRWB ;Set up sector 0 addresses MULWTC::MOV #MD.CMP,IOMOD ;Set compare modifier MULWT:: ;Perform multiple-copy write to disk PUSH ;Save LBN at entry PUSH ;Save registers MOV ACTBBR,R2 MOV BB.RCT(R2),R3 ;Get table size CLR R0 ;Get ready for the BISB BISB BB.COP(R2),R0 ;Set number of copies MOV R0,R4 ;Initialize the error count MOV #OP.WR,IOFUN ;Set function code to write physical 10$: MOV IOMOD,-(SP) ;Save if have to call DSKIO > 1 time MOV R2,-(SP) MOV #BBR.IO,R2 ;if this not the last try, then we CMP R0,#1 ;must set the ignore bit BEQ 20$ BIS #IGN.IO,R2 20$: CALL DSKIO ;Perform the write MOV (SP)+,R2 ;MOV doesn't affect the C-bit MOV (SP)+,IOMOD ;Restore modifier flags BCC 40$ ;If CC success CMPB IOST,#ST.DAT ;Should we rewrite with forced error BNE 60$ ;drive or contlr problem! exit BIS #MD.ERR,IOMOD ;Set forced error modifier CALL DSKIO ;Write it back with forced error set. ; We have to do this to make sure that ; subsequent access does not succeed. ; This block no longer reflects the ; true state of the RCT tables! 30$: DEC R4 ; Count the error. 40$: ADD R3,LBNL ;Get address of sector in next copy ADC LBNH ;... SOB R0,10$ ;branch if not done .IF EQ DU$ERL-2 CMP R4,#1 BNE 50$ MOV #DTEPAK,R0 ;In here if only one write succeeded, $REL .-2 DTEPAK UMY ;which must be reported as a NO- MOV #MF.NMP,L.EVNT(R0) ;-MULTICOPY PROTECTION error to the CALL DO.DTE ;error logger. 50$: .ENDC ;EQ DU$ERL-2 CLC TST R4 ;Did the write succeed to any copy? BNE 70$ ;If NE yes (TST cleared C) 60$: SEC ;Otherwise, indicate failure 70$: POP ;*C* Restore registers POP ;*C* Restore LBN at entry RETURN ; .DSABL LSB .SBTTL MULRWB - set up sector zero addresses ;+ ; MULRWB - set up sector zero addresses ; used by MUL(RD/WT)B ;- .ENABL LSB MULRWB::MOV #SEC0,DSKBF ;SEC0 = buffer to receive (virt addr) $REL .-4 SEC0 UMY MOV R2,-(SP) MOV ACTBBR,R2 MOV BB.MAX(R2),LBNL ;Set disk address (LBN) low order MOV BB.MAX+2(R2),LBNH ;... and high order MOV (SP)+,R2 RETURN .DSABL LSB .SBTTL NEWBF - *BBR* Set QIO Parameters for New RBN .SBTTL OLDBF - *BBR* Set QIO Parameters for Old RBN ;+ ; NEWBF - Set QIO Parameters for New RBN ; OLDBF - Set QIO Parameters for Old RBN ; ; These routines set the I/O Parameters necessary for updating the Replacement ; Control Table sectors for the new and old RBNS. ; ; Input: ; None ; ; Output: ; The I/O parameters are updated with the LBN and buffer address. ; R0 = Buffer Address ;- .ENABL LSB NEWBF:: MOV #RCTBF,R0 ;Store the buffer address $REL .-2 RCTBF UMY MOV NLBNL,LBNL ;Put the LBN (Lo) in the DPB MOV NLBNH,LBNH ;Put the LBN (Hi) in the DPB BR 10$ ; OLDBF:: MOV #RCTB1,R0 ;Store the buffer address $REL .-2 RCTB1 UMY MOV OLBNL,LBNL ;Put the LBN (Lo) in the DPB MOV OLBNH,LBNH ;Put the LBN (Hi) in the DPB 10$: MOV R0,DSKBF ;Put the buffer address in the DPB RETURN ; .DSABL LSB .SBTTL P2REC - *BBR* Restore Data for PHASE 2 Recovery ;+ ; P2REC - Restore Data for PHASE 2 Recovery ; ; This routine is called when RCT is bringing a unit ONLINE which went ; OFFLINE during PHASE 2 of a Bad Block Replacement. Data which describes ; the replacement which was in progress is restored to RCTDAT'S internal ; buffer from Sector 0 of the disk's Replacement Control Table. ; ; Input: ; R0 = Buffer Address of data read from Sector 0 of table (RCTFLG) ; ; Output: ; RCTFLG= Internal Flag Word ; BLBNL = Bad Block Number, Lo order ; BLBNH = Bad Block Number, Hi order ; NRBNL = New RBN, Lo order ; NRBNH = New RBN, Hi order ; NLBNL = Sector in table of New RBN, Lo order ; NLBNH = Sector in table of New RBN, Hi order ; NLBNO = Offset in sector of New RBN ; ORBNL = Old RBN, Lo order (if any) ; ORBNH = Old RBN, Hi order (if any) ; OLBNL = Sector in table of Old RBN, Lo order (if any) ; OLBNH = Sector in table of Old RBN, Hi order (if any) ; OLBNO = Offset in sector of Old RBN (if any) ;- P2REC:: PUSH ;Phase 2 recovery: save registers ; Restore Flag Word and Bad Block Number CALL HASH ;Restore PLBNL, PLBNH ; Restore New RBN ADD #8.,R0 ;R0 -> RBN low in SEC0 MOV #NRBNL,R3 ;Point to internal storage area $REL .-2 NRBNL UMY MOV @R0,(R3)+ ;Restore NRBNL MOV (R0)+,R2 ;And set up for divide MOV (R0),(R3)+ ;Restore NRBNH MOV (R0)+,R1 ;And set up for divide PUSH ;Save sector 0 pointer CALL RBNDIV ;Restore NLBNL, NLBNH, NLBNO ; Restore Old RBN (if any) POP ;Restore sector 0 pointer MOV @R0,(R3)+ ;Restore ORBNL MOV (R0)+,R2 ;And set up for divide MOV @R0,(R3)+ ;Restore ORBNH MOV (R0)+,R1 ;And set up for divide BNE 10$ ;If NE there is one TST R2 ;Are both Hi and Lo zero? BEQ 20$ ;If EQ yes, there isn't one 10$: CALL RBNDIV ;Restore $OLBNL, $OLBNH, $OLBNO 20$: POP ;Restore registers RETURN ; ; ; Routine to divide RBN by 128 to find sector and offset in Replacement ; Control Table (used by P1REC and P2REC only) ; RBNDIV: MOV #128.,R0 ;Divisor = 128. = R0 ;Dividend = RBN (hi) = R1 ;Dividend = RBN (lo) = R2 CALL DDIV ;Do the division MOV R2,(R3)+ ;Restore sector, lo (R2= quot, lo ord) MOV R1,(R3)+ ;Restore sector, hi (R1= quot, hi ord) ASH #2,R0 ;Multiply remainder by 4 MOV R0,(R3)+ ;Restore offset (R0 = remainder*4) MOV ACTBBR,R5 ;R5 -> current entry ADD BB.MAX(R5),-6(R3) ;Add in max possible LBN ADC -4(R3) ;... so we have a real LBN to RCT ADD BB.MAX+2(R5),-4(R3) ;Don't forget high word for LBN ADD #2,-6(R3) ;Account for SEC0 & sec1 of the RCT ADC -4(R3) ;... RETURN ; .DSABL LSB .SBTTL SRCH - *BBR* Replacement Control Table Search Algorithm ;+ ; SRCH - Replacement Control Table Search Algorithm ; ; This algorithm begins at the primary RBN descriptor (found by HASH) and ; searches the table until an appropriate RBN is found for the Bad LBN. ; ; If the primary RBN descriptor is not empty (or the desired LBN is not ; stored there), then a PING PONG search of the block containing the primary ; descriptor ensues. If an empty descriptor (or the desired LBN address) is ; not found, then a linear scan of the remaining blocks of the table (and ; descriptors within the blocks) is begun. That linear scan begins at the ; next highest block and wraps around at the end of the table to the first ; block of the table to contain descriptors. ; ; The search ends when one of the following occurs: ; ; 1. An unallocated RBN descriptor is found at the location provided by ; HASH - a Primary. ; ; 2. An unallocated RBN descriptor is found by the search - a Secondary. ; ; 3. The desired LBN address is encountered at one of the allocated ; RBN descriptors - a Match. ; ; 4. The entire table is searched without success - a Failure. ; ; 5. A block of the table can not be read from any copy - a Failure. ; ; Input: ; PLBNL = Blk number in first copy of table for Primary RBN, Lo order ; PLBNH = Blk number in first copy of table for Primary RBN, Hi order ; PLBNO = Offset within above block to the Primary RBN descriptor ; MXLBN = Maximum user addressable LBNs ; ; Output: ; ; NRBNL = New RBN (which will replace LBN), Low order ; NRBNH = ... and High order ; NLBNL = LBN in table where SRCH found new RBN, Low ; NLBNH = ... and High order ; NLBNO = Offset in NLBNH:NLBNL of new RBN descriptor ; ORBNL = Old RBN (which previously replaced LBN), Low ; ORBNH = ... and High order ; OLBNL = LBN in table where SRCH found old RBN, Low ; OLBNH = ... and High order ; OLBNO = Offset in OLBNH:OLBNL of old RBN descriptor ; ; C = 0 if Success ; C = 1 if Failure (table was full or unable to read table) ; ; ; The RCT search can return a failure status for any of the following ; reasons. ; ; I. All the descriptors have been tested and no unused descriptor ; can be found. In this case, A Disk Transfer Error error log ; packet will be formed with a Media Format Error code, subcode ; "No Replacement Block Available," and with the "Error During ; Replacement" error log message flag set. This is case (4), ; above. ; ; The BBR attempted packet will indicate "No Replacement Block ; Available" event subcode, with the "Error During Replacement" ; message flag set, the "Replacement Attempted" flag set, and, ; if the block was previously replaced, "the Bad RBN" flag set. ; ; II. The RCT is found to be corrupted ; ; III. A MULT(R/W) of the RCT failed. This is case (5), above. ; ; on I/O failure, we return with R0 -> to a BBR packet to log. ;- .ENABL LSB SRCH:: .IF NE DU$BRD MOV R0,-(SP) .ADDR #STEPCH,R0 .PRINT MOV (SP)+,R0 .ENDC ;Search replacement control table PUSH ;Save registers CLR ORBNL ;Assume no match CLR ORBNH ;... CLR R0 ;R0 = Null descriptor found indicator MOV PLBNO,R4 ;R4 = starting offset in block (bytes) ASR R4 ;Convert it into words, and then ASR R4 ;..into longwords for Ping-Pong search MOV #RCTBF,DSKBF ;User RCTBF buffer address $REL .-4 RCTBF UMY MOV PLBNL,LBNL ;Get block number in table MOV PLBNH,LBNH ;... and the high order SNEXT: CLR IOMOD ;Say compare modifier CALL MULRD ;Read the block from the table BCC 20$ .IF EQ DU$ERL-2 MOV #BBRPAK,R0 ;Replacement Control Table is trashed $REL .-2 BBRPAK UMY MOV #BB.RCI,L.EVNT(R0) ;event code: No Replacement Block BISB #LF.RCT,L.FLGS(R0) ;set error during replacement flag BIS #LFR.RI,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG ;should we set Bad RBN replace flag? BEQ 10$ ;branch if not BIS #LFR.BR,L.RPFL(R0) 10$: SEC .ENDC ;EQ DU$ERL-2 JMP SXIT ;If CS we failed to read any copy 20$: CLR R2 ;R2 = delta from starting offset STEST: MOV R4,R1 ;Get starting offset ADD R2,R1 ;Offset = starting offset + delta2 BLT SBMP ;If LT offset < 0 CMP R1,#127. ;Is offset > 127 ? BGT SBMP ;If GT yes ASL R1 ;Change longword offset to byte offset ASL R1 ;... ;next instruction asks: Unallocated RBN descriptor? BIT #,RCTBF+2(R1) $REL .-2 RCTBF+2 UMY BNE SALL0 ;branch if allocated, and ;continue searching ;in here if we have found an unallocated descriptor, ;we now return (successfully) and use it. MOV #NRBNL,R3 ;Show we have a new RBN $REL .-2 NRBNL UMY CALL SRBN ;Calculate the new RBN CLC ;Indicate success JMP SXIT ;And exit SALL0: BIT #DF.ALL,RCTBF+2(R1) ;Is this RBN allocated? $REL .-2 RCTBF+2 UMY BEQ SNULL ;branch if not allocated, ;and go check for null entry MOV RCTBF+2(R1),R3 ;Get working copy of descriptor high $REL .-2 RCTBF+2 UMY BIC #,R3 ;Clear flag bits CMP BLBNH,R3 ;Allocated to LBN we are replacing? BNE SNULL ;branch if no, check for null entry CMP BLBNL,RCTBF(R1) ;Maybe - how about low order word? $REL .-2 RCTBF UMY BNE SNULL ;branch if no, check for null entry ;in here if this descriptor revectors the same LBN that ;we are currently replacing; i.e., if the LBN we are replacing ;has already once been replaced BIS #RF.BR,SEC0+RCTFLG MOV #ORBNL,R3 ;Show we have an old RBN $REL .-2 ORBNL UMY CALL SRBN ;Calculate the old RBN BR SBMP ;And continue searching ;ACTION: does this search implementation waste time waiting for ; the second NULL descriptor ?????????????????????????? SNULL: TST RCTBF+2(R1) ;Is this a null entry (DF.NUL set)? $REL .-2 RCTBF+2 UMY BPL SBMP ;branch if not a null entry TST R0 ;Is this the first time we have ;bumped into a null entry? BEQ SNULL2 ;branch if yes .IF EQ DU$ERL-2 MOV #DTEPAK,R0 ;this is reported as a disk transfer $REL .-2 DTEPAK UMY ;error. MOV #MF.NRP,L.EVNT(R0) ;Media Format Error: subcode No ;Replacement Block Available CALL DO.DTE ;send the disk transfer message MOV #BBRPAK,R0 $REL .-2 BBRPAK UMY MOV #BB.NRP,L.EVNT(R0) ;event code: No Replacement Block BISB #LF.RCT,L.FLGS(R0) ;set error during replacement flag BIS #LFR.RP,L.RPFL(R0) ;replacement attempted replace flag BIT #RF.BR,SEC0+RCTFLG ;should we set Bad RBN replace flag? BEQ 30$ ;branch if not BIS #LFR.BR,L.RPFL(R0) 30$: .ENDC ;EQ DU$ERL-2 SEC BR SXIT SNULL2: INC R0 ;increment counter MOV ACTBBR,R4 ;R4 will be cleared below MOV BB.MAX(R4),LBNL ;Get max user addressable LBN - low MOV BB.MAX+2(R4),LBNH ; max user addressable LBN - high ADD #2,LBNL ;Start at table sector 3, which is ADC LBNH ;the beginning of the RBN table CLR R4 ;Zero starting offset BR SNEXT ;go start scanning the RBN table SBMP: ;Bump the delta NEG R2 ;Delta = -delta BMI STEST ;If MI delta < 0 INC R2 ;Delta = delta + 1 CMP R2,#128. ;Is delta < 128 ? BLT STEST ;If LT yes, continue in this sector CLR R4 ;Zero starting offset ADD #1,LBNL ;Prepare to read the next sector ADC LBNH ;... JMP SNEXT ;Go read the next sector ;Calc RBN for primary, secondary, or match SRBN: PUSH ;Save registers ;Calculate RBN ($RBNL,$RBNH) ;RBN=((Final LBN-(MXLBN+2))*128)+OFFSET MOV LBNL,R3 ;Start w/resultant blk in table, lo ord MOV LBNH,R2 ;... and high order MOV ACTBBR,R0 ;Use R0 temporary SUB BB.MAX(R0),R3 ;Subtract # LBNs in host area, lo SBC R2 ;... and the carry SUB BB.MAX+2(R0),R2 ;Subtract # LBNs in host area, hi SUB #2,R3 ;Subtract 2 (for RCT sectors 0 and 1) SBC R2 ;... and the carry MOV #128.,R0 ;128 = multiplicand CALL DMUL ;Perform double precision multiply ;R0=(Final LBN-(MXLBN+2))*128, Hi ord ;R1=(Final LBN-(MXLBN+2))*128, Lo ord MOV 10(SP),R2 ;Get saved offset value ASR R2 ;Change byte offset to longword offset ASR R2 ;... ADD R2,R1 ;R1=((Final LBN-(MXLBN+2))*128)+OFFSET, Lo ADC R0 ;R0=((Final LBN-(MXLBN+2))*128)+OFFSET, Hi MOV 4(SP),R3 ;Get saved RBN addr ($NRBNL or $ORBNL) MOV R1,(R3)+ ;Store RBN value, low order MOV R0,(R3)+ ;... and high order MOV LBNL,(R3)+ ;Store block in table, low order MOV LBNH,(R3)+ ;... and high order ASL R2 ;Change longword offset to byte offset ASL R2 ;... MOV R2,@R3 ;Store offset SXIT: POP ;Restore registers RETURN ;Return from SRCT or $SRCH .ENABL LSB .SBTTL UNALL - *BBR* Mark RBN Unallocated ;+ ; UNALL - Mark RBN Unallocated ; ; This routine is called when a failure has occurred which requires RCT ; to restore the Replacement Control Table to its original contents. ; If RCT has allocated a new RBN, this routine marks that RBN as unallocated. ; If RCT has marked an old RBN as unusable, this routine marks it as once ; again allocated. The Bad LBN is left in its original state (either not ; replaced or revectored to the old RBN). ; ; Input: ; RCT SEC0 ; Internal LBN, RBN, ETC. Storage Area ; ; Output: ; The New RBN is marked as unallocated. ; The Old RBN is marked as allocated. ;- .ENABL LSB UNALL:: PUSH ;Save registers CALL NEWBF ;Set up DPB for new RBN ;R0 = RCTBF MOV R0,DSKBF ;Set buffer address CALL MULRDC ;Read RCT block (Compare) MOV R0,R5 ;Get buffer address ADD NLBNO,R5 ;Form RBN descriptor address CLR @R5 ;Show the new RBN is unallocated CLR 2(R5) ;... CALL MULWTC ;Write out block of new RBN (Compare) BCS 30$ BIT #RF.BR,SEC0+RCTFLG ;Is there an old RBN BEQ 30$ ;If EQ no CALL OLDBF ;Set up DPB for old RBN ;R0 = RCTB1 MOV R0,DSKBF ;Set buffer address for old MOV #BBR.IO,R2 CALL MULRDC ;Read in RCT block for old (Compare) MOV R0,R5 ;Get buffer address ADD OLBNO,R5 ;Form RBN descriptor address MOV BLBNL,(R5)+ ;Mark old RBN allocated to bad LBN MOV BLBNH,@R5 ;... BIS #DF.ALL,@R5 ;Indicate that it is allocated CMP PLBNO,OLBNO ;Offsets into RCT block same? BNE 10$ ;No, was secondary CMP PLBNL,NLBNL ;Was it primary? BNE 10$ ;No, must have been secondary CMP PLBNH,NLBNH ;Was it? BEQ 20$ ;No 10$: BIS #DF.SEC,@R5 ;Indicate seconday 20$: MOV #BBR.IO,R2 CALL MULWTC ;Set up DPB for new RBN (Compare) 30$: POP ;Restore registers RETURN .DSABL LSB .SBTTL WTSAV - *BBR* Write Saved Data Back Out To Disk ;+ ; WTSAV - Write Saved Data Back Out To Disk ; ; This routine will write-compare the data previously read from the bad LBN ; back out to the LBN (or the RBN if the LBN has been replaced). The ; "Force-Error" modifier is used if the saved data is invalid. The QIO is ; considered successful if either the write-compare succeeds, or (if the ; data is invalid) only a "Force-Error" is returned (IS.RDD). ; ACTION: is the above correct????????????????????? ; ; Input: ; BLBNL = Bad LBN (Low order) ; BLBNH = Bad LBN (High order) ; ; Output: ; The saved data is written to the LBN. ; C = 0 if Success (IS.SUC or IS.RDD) ; C = 1 if Failure ;- WTSAV:: MOV R2,-(SP) ;Write saved data back to LBN CLR IOMOD ;No modifiers yet MOV BLBNL,LBNL ;Put LBN in DPB MOV BLBNH,LBNH ;... MOV #OP.WR,IOFUN ;Write function BIT #RF.FE,SEC0+RCTFLG ;Is saved data invalid? BEQ 10$ ;If EQ no BIS #MD.ERR,IOMOD ;Write with Forced-Error modifier 10$: MOV #LBN,DSKBF ;Set address of saved data $REL .-4 LBN UMY CLR R2 CALL DSKIO ;Perform the write MOV (SP)+,R2 RETURN ; .SBTTL DDIV - *BBR* Double Precision Divide Routine ;+ ; DDIV - Double Precision Divide Routine ; ; Inputs: R2= Low order dividend ; R1= High order dividend ; R0= Divisor, (16 bits unsigned) ; ; Outputs: R2= Low order of quotient ; R1= High order of quotient ; R0= Remainder ;- DDIV:: MOV R3,-(SP) ;Save R3 MOV #32.,R3 ;Set iteration count in R3 MOV R0,-(SP) ;Put divisor on stack CLR R0 ;Set remainder to zero 10$: ASL R2 ;Shift the entire dividend ROL R1 ;.. one bit to the left and ROL R0 ;.. into the remainder CMP R0,(SP) ;Is remainder .GE. divisor? BLO 20$ ;No, skip to iteration control SUB (SP),R0 ;Yes. Subtract divisor out INC R2 ;And increment the quotient 20$: SOB R3,10$ ;Repeat as long as necessary TST (SP)+ ;Purge divisor from stack MOV (SP)+,R3 ;Restore R3 RETURN ;Return to caller .SBTTL DMUL - *BBR* Double Precision Multiply ;+ ; DMUL - Double Precision Multiply ; ; Inputs: R0 = Single precision magnitude multiplier ; R2,R3 = Double precision magnitude multiplicand ; ; Outputs: C = 0 ; R0,R1 = Double precision magnitude result ; R2,R3 = Altered ; R4,R5 = Preserved ;- DMUL:: MOV R0,-(SP) ;Single precision multiplier CLR R0 ;Init the result CLR R1 ; 10$: TST (SP) ;If remaining multiplier is zero BEQ 30$ ;Then all through ROR (SP) ;If next bit is a one BCC 20$ ; ADD R3,R1 ;Then add multiplicand to result ADC R0 ; ADD R2,R0 ; 20$: ASL R3 ;In either case, double multiplicand ROL R2 ; BR 10$ ;And see if any more multiplier 30$: TST (SP)+ ;Clean up the stack, return C=0 RETURN ; .SBTTL RCONTX - *BBR* Restore the Context Before the Interrupt. ;+ ; ;- .ENABL LSB RCONTX: MOV (SP)+,(PC)+ ;Store return addr... RBACK: .WORD 0 ;...here MOV ACTBBR,R0 MOV BB.BUF(R0),R5 MOV BB.RNG(R0),R4 MOV BB.POR(R0),R3 .IF EQ DU$ERL-2 CALL GETMES ;move the end message into its own .ENDC ;EQ DU$ERL-2 ;buffer so that the ring descriptor ;can be freed. ;now we need to call GETNEXT to return the UQSSP descriptor ;to the controller, and then clear the BBRON flag because ;there is no longer a BBR I/O outstanding. These things ;must be done inside a P1EXT block because we are in UMY ;and they are in UMX MOV #GETNEXT,R2 $REL .-2 GETNEXT UMX MOV #BBRON,R1 $REL .-2 BBRON UMX .YP1EX P1UMX CALL @R2 ;then call Getnext CLR @R1 ;Clear the the BBR flag .YP1EN MOV #CONTEXT,R5 ;R5 -> context value $REL .-2 CONTEXT UMY MOV (R5)+,R0 ;R0 = context ptr BEQ 20$ ;Branch if no context CLR CONTEXT ;Clear until saved again MOV -(R0),R4 ;Restore registers MOV -(R0),R3 ;R3 too MOV -(R0),R2 ;R2 too MOV -(R0),R1 ;R1 too 10$: MOV -(R0),-(SP) ;Build stack again CMP R0,R5 ;Did we pop all of them? BNE 10$ ;No, keep poping CALLR @(SP)+ ;Goto...who knows/cares... 20$: CALLR @RBACK ;Return to caller .DSABL LSB .SBTTL SCONTX - *BBR* Save the Context - GO START COMMAND! ;+ ; ;- .ENABL LSB SCONTX: MOV #CONTEXT,R0 ;R0 -> first entry to store $REL .-2 CONTEXT UMY ADD #2,R0 MOV (SP)+,(R0)+ ;Store return - (last) 10$: MOV (SP)+,(R0)+ ;Store arguments... CMP SP,SAVESP ;...till the beginning BNE 10$ ;Repeat till done MOV R1,(R0)+ ;Store register R1 MOV R2,(R0)+ ;R2 ditto MOV R3,(R0)+ ;R3 ditto MOV R4,(R0)+ ;R4 too! MOV R0,CONTEXT ;Start restoring here! MOV #BBRON,R1 $REL .-2 BBRON UMX .YP1EX P1UMX MOV #-1,@R1 ;Indicate BBR is on .YP1EN MOV ACTBBR,R3 MOV BB.POR(R3),R3 ;POLL wants R3 -> port control entry MOV #POLL,-(SP) ;go poll the controller to start $REL .-2 POLL UMX ;the command JMP UMYRET CONTEX::.WORD 0 ;Save ptr here! .REPT 100 ;Size of internal stack .WORD 125252 ;Locations .ENDR .DSABL LSB .IF NE DU$ERL-2 .SBTTL INTEGA - *BBR* Integrity Checks On RCT Block 0 .ENABL LSB INTEGA: MOV #SEC0+RCTFLG,R0 ;R0 = address of sector 0 flag $REL .-2 SEC0+RCTFLG UMY PUSH ;Save R1,R0 MOV (R0)+,R1 ;R1 = flagword BIC #,R1 ;Clear out possible bits ;TST R1 ;Any bad bits set ? BNE 100$ ;Yes, error, invalid RCT TST @R0 ;Reserved word 0 ? BNE 100$ ;No, error, invalid RCT BIT #RF.P1,-(R0) ;Phase 1 bit set ? BEQ 30$ ;No branch BIT #RF.P2,@R0 ;Phase 2 bit set ? BNE 100$ ;Yes, error, invalid RCT: both bits on BIT #RF.BR,@R0 ;BR bit set ? BNE 100$ ;Yes, error, invalid RCT ADD #RRBNH-RCTFLG,R0 ;R0 -> Hi bad RBN value TST @R0 ;Hi bad RBN value given ? BNE 100$ ;Yes, error, invalid RCT TST -(R0) ;Bad RBN value given ? BNE 100$ ;Yes, error, invalid RCT TST -(R0) ;Hi RBN value given ? BNE 100$ ;Yes, error, invalid RCT TST -(R0) ;RBN value given ? BNE 100$ ;Yes, error, invalid RCT CMP -(R0),-(R0) ;R0 -> RLBNL TST (R0)+ ;LBN given ? BNE 10$ ;Yes, branch TST @R0 ;Well was there a high ? BEQ 100$ ;No, error, invalid RCT 10$: CMP @R0,BB.MAX+2(R2) ;Is hi LBN in SEC0 too big ? BHI 100$ ;Yes, error, invalid RCT BEQ 20$ ;Maybe, check low BR 90$ ;All set, no errors. 20$: CMP -(R0),BB.MAX(R2) ;Is low LBN in SEC0 too big ? BHI 100$ ;Yes, error, invalid RCT BR 90$ ;All set, no errors. ; PHASE 2 Integrity Checks: First see if doing PHASE 2 30$: BIT #RF.P2,@R0 ;Doing phase 2 ? BEQ 90$ ;No, all set CMP (R0)+,(R0)+ ;R0 -> RLBNL TST (R0)+ ;LBN specified ? BNE 40$ ;Yes, branch TST @R0 ;Hi LBN specified ? (RLBNH) BEQ 100$ ;No, error,invalid RCT 40$: CMP @R0,BB.MAX+2(R2) ;Is hi LBN too big ?(RLBNH) BHI 100$ ;Yes, branch, invalid RCT BEQ 50$ ;Go check low LBN BR 60$ ;Not too big, branch 50$: CMP -2(R0),BB.MAX(R2) ;Is low too big ? (RLBNL) BHI 100$ ;Yes, error, invalid RCT 60$: ADD #RRBNL-RLBNL,R0 ;R0 -> RRBNL BIT #RF.BR,@0(SP) ;BR bit set ? (SP -> addr of RCTFLG) BNE 80$ ;Yes, branch. Check for value 70$: TST (R0)+ ;A value in bad RBN ? BNE 100$ ;Yes, error, invalid RCT TST @R0 ;Bad RBN high field = 0? BNE 100$ ;No, error, invalid RCT BR 90$ ;All set, no errors. 80$: TST (R0)+ ;In SEC0, bad RBN field = 0 ? BNE 90$ ;No, branch. All fine TST @R0 ;How about hi word = 0 ? BEQ 100$ ;Yes, error, invalid RCT 90$: POP ;Restore R1 CLC RETURN ;And return to caller 100$: POP ;Restore R0,R1 SEC ;Set carry to say invalid RCT RETURN ;Return to caller .DSABL LSB .ENDC ;NE DU$ERL-2 .IF NE DU$BRD S3: .ASCII /BBR STEP 3/<15><12><0> S4: .ASCII /BBR STEP 4/<15><12><0> S5: .ASCII /BBR STEP 5/<15><12><0> S6: .ASCII /BBR STEP 6/<15><12><0> S7: .ASCII /BBR STEP 7/<15><12><0> S8: .ASCII /BBR STEP 8/<15><12><0> S9: .ASCII /BBR STEP 9/<15><12><0> S10: .ASCII /BBR STEP 10/<15><12><0> S11: .ASCII /BBR STEP 11/<15><12><0> S12A: .ASCII /BBR STEP 12A/<15><12><0> S12B: .ASCII /BBR STEP 12B/<15><12><0> S12C: .ASCII /BBR STEP 12C/<15><12><0> S13: .ASCII /BBR STEP 13/<15><12><0> S14: .ASCII /BBR STEP 14/<15><12><0> S15: .ASCII /BBR STEP 15/<15><12><0> S16: .ASCII /BBR STEP 16/<15><12><0> STEPCH:: .ASCII /BBR SEARCH/<15><12><0> .even .ENDC ;NE DU$BRD .ENDC ;NE DU$BBR .SBTTL xx - PSECT Calculations ;******************************************************************** .PSECT UMLST ;******************************************************************** LMEND:: LMSIZE ==: LMEND - UM.LST ;size in bytes: psect UMLST ;******************************************************************** .PSECT UMY ;******************************************************************** UMYEND:: UMYSIZ ==: UMYEND - UMYBAS ;size in bytes: psect UMY .ASSUME UMYSIZ LE 8192.,<;BBR Psect is too big> ;********************************************************************* UMPSECT ;********************************************************************* .IF NE MMG$T ; UMX Calculations ;********************************************************************* UMXPSECT ;********************************************************************* UMXEND:: ;********************************************************************* .PSECT UMXDAT ;********************************************************************* UMXDND:: ;********************************************************************* .PSECT UMDATA ;********************************************************************* DATAND:: UMXDSZ ==: UMXDND - UMXDBA ;Size in bytes: Psect UMXDAT DATASZ ==: DATAND - DATABA ;Size in bytes: Psect UMDATA UMXSZ ==: UMXEND - UMXBA ;Size in bytes: Psect UMX ;Size in bytes: ALL XM PSECTS UMXSIZ ==: UMXDSZ+DATASZ+UMXSZ .IF NE DU$BBR ;the XM handler has two distinct PAR1 spaces. their code, however, is ;contiguous in the image DUX.SYS. the first PAR1 space is the code ;comprised of the consecutive psects UMXDAT, UMDATA, and UMX. the second ;PAR1 space, intended for the implementation of the Host Initiated BBR ;algorithm, is comprised of the psect UMY. Because these two PAR1 ;segments are assumed to be contiguous by the INSTALL/LOAD/FETCH code, ;the UMY psect must begin on a 64 byte boundry, which forces the first ;word of the UMY psect to be at virtual address 20000. Therefore, a bit ;of padding at the end of the UMX psect is inserted, making it end at ;a 64 byte boundry - the psect UMXPAD is this padding. PADLEN==: <<</100>*100>-UMXSIZ> .IFF ;NE DU$BBR PADLEN ==: 0 ;no affect in non-XM .ENDC ;NE DU$BBR MEMUMX ==: /100 ;MEMUMX is the required global region size ;(Add 77 to be sure we got it all) XMLEN ==: UMXSIZ+PADLEN+UMYSIZ ;length of Global Region which we shall allocate .IF NE DU$BBR ;***************************************************************************** .PSECT UMXPAD ;***************************************************************************** .BLKB PADLEN .ENDC ;NE DU$BBR ;**************************************************************************** .PSECT UYRLST ;**************************************************************************** UYREND:: UYRLSZ ==: UYREND - UYR.LST ;***************************************************************************** .PSECT UMRLST ;***************************************************************************** UMREND:: UMRLSZ ==: UMREND - UMR.LST ;Size in bytes: Psect UMRLST UMLSZ ==: UMRLSZ + UYRLSZ ;Size in bytes: list psects ;***************************************************************************** .PSECT UMNDLS ;List end Psect ;***************************************************************************** ; UMPDSZ ==: 1000-/1000>>> UMPDSZ ==: 1000-/1000>>> UMPAD:: .BLKB UMPDSZ ;PAD to block align SETOV1 ;***************************************************************************** .PSECT LDOVR ;***************************************************************************** . = . + UMLSZ .IFF ;NE MMG$T UMLSZ == 0 .ENDC ;NE MMG$T ;***************************************************************************** .PSECT UMDVR ;***************************************************************************** SIZE = . SIZED = /2 ;********************************************************************* .PSECT UMLORD ;********************************************************************* ;Establish size of low memory data LOWDATA ==: . - LOWBAS ;Bytes ;;; .ASSUME . LE UMSTRT+1000 ;SET CODE access range .IF NE MMG$T .ASSUME LOWDATA EQ UMXDSZ ;Hi & Low Order Dependent Psects .ENDC ;NE MMG$T ;********************************************************************* UMPSECT ;********************************************************************* .SBTTL .DRBOT - *BOOT* PRIMARY Bootstrap ;+ ; This code implements the standard RT-11 bootstrap. ;- .DRBOT UM,BOOT1,READ,CONTROL=,PSECT=UMLCODE . = UMBOOT+40 BOOT1: CALLR @#BOOT-UMBOOT ;Start the boot. .SBTTL LOACSR - *BOOT* Load IP Register From Tables ;+ ; The following routine checks the unit table for the corresponding ; unit/port - which then is used to get the IP location for that port - ; which gets loaded in the proper location. In total - The DU handler ; will support multi-port booting! ;- .ENABL LSB . = UMBOOT + 14 LOACSR: ASL R3 ;Adjust (size of entry = 4) ASL R3 ;R3 = offset into Unit/Port table ADD #PD.POR-UMBOOT,R3 ;R3 -> Unit/Port table entry BR 10$ ;"Dynamite - jump!" . = UMBOOT + 44 10$: MOV (R3)+,BUNIT ;Store MSCP Unit; ptr to Port MOVB (R3)+,BPART ;Get partition # of boot volume MOVB @R3,R3 ;Get Port into R3 BR 20$ ;"Dynamite - jump!" . = UMBOOT + 70 20$: ASL R3 ;R3 = offset into Port/CSR table ADD #PD.CSR-UMBOOT,R3 ;R3 -> CSR for this Port BR 30$ ;"Dynamite - jump!" . = UMBOOT + 104 30$: MOV @R3,BDUAIP ;Store the CSR into proper location CALLR ALTCHK ;Check/alter boot CSR ; due to alternate "standard" .DSABL LSB . = UMBOOT+120 READ: MOV #NRETRY,BRETRY ;Set up a retry count. ASL R1 ;Convert word count to byte count ASR #1 ;Have we done 1 time code yet? BCC READ1 ;Yes, skip ! TST HRDBOT ;Is this a hardware boot? BEQ READA ;Yes, then unit is set up. MOV @#B$DEVU,(PC)+ ;No, then specify the unit. BUNIT: .WORD 0 ;Default is unit 0. READA: MOV BUNIT,R3 ;R3 = unit number CALL LOACSR ;Go figure out the IP location ; and check for alternate CSR .BR BINIT .SBTTL BINIT - *BOOT* Port Initialization ;+ ; Port initialization ;- BINIT: DEC (PC)+ ;Have we retried the init enough? BRETRY: .WORD 0 ;Retry count in boot. BLT BIOELK ;Yes, quit with failure. MOV BDUAIP,R5 ;Point to the CSR. MOV R4,(R5)+ ;Strobe the AIP to start the init. MOV #ISTEP1,R3 ;Step bit to check is Step 1. MOV #BINLST-UMBOOT,R4 ;Point to list of words to load. 10$: TST @R5 ;Error bit up in the ASA? BMI BINIT ;Retry Port initialization on error. BIT @R5,R3 ;Is the desired step bit on?. BEQ 10$ ;No, wait for it. MOV (R4)+,@R5 ;Set the next word. ASL R3 ;Next time, next step. BPL 10$ ;Not Step 4, wait for this step. CMP (R4)+,(R4)+ ;Bump over interrupt identity area. MOV R4,BCRING ;Rings now active. CRING -> buffer. MOV R4,BMRING ;MRING -> same buffer!!! JSR R0,BGTBUF ;Get and clear the command buffer. .WORD OP.ONL ;Set up online command. JSR PC,BDOIO ;Bring the unit online. .BR READ1 ;All ok, continue the real read. .SBTTL READ1 - *BOOT* Bootstrap Read Routine ;+ ; Perform a real read (versus an init) of the disk device. ;- .ENABL LSB READ1: JSR R0,BGTBUF ;Get and clear the command buffer. .WORD OP.RD ;Set up read command. MOVB #.-.,P.PART(R4) ;Set high order bits of LBN. BPART=: .-4 ;Partition # of boot volume MOV R0,P.LBN(R4) ;Set low order bits of LBN. MOV R1,P.BCNT(R4) ;Set byte count in command buffer. MOV R2,P.BUFF(R4) ;Set buffer address. .BR BDOIO ;Go do bootstrap I/O. BDOIO: MOV (PC)+,R3 ;Point to the port CSR. BDUAIP: .WORD -1 ;ADDR OF AIP CSR (FROM PD.CSR) 10$: MOV #BCRING+2-UMBOOT,R4 ;Point to the command ring word 2. MOV #OWN,@R4 ;Give the command buffer to the Port. MOV (R3)+,R5 ;Read the UDAIP to start polling. 20$: TST @R3 ;Check if UDA still online. BMI 40$ ;No, need to retry init. TST @R4 ;Did the Port take the packet? BMI 20$ ;Nope, spin on a wait. TST -(R4) ;Back up the pointer. MOV #OWN,-(R4) ;Give the Port a reply buffer. 30$: TST @R3 ;Is the UDA running?. BMI 40$ ;No, need to retry init. TST @R4 ;Wait for the Port to answer back. BMI 30$ ;Loop until end packet given back. TSTB BBUFF+P.STS ;Was there any error at all?. BIOELK: BNE BIOERR ;If so, it can't be recovered!! RETURN ;Return, no error. 40$: TST (SP)+ ;Adjust the stack. BR BINIT ;Error so try another init. .DSABL LSB .SBTTL BGTBUF - *BOOT* Clear MSCP Rings, Do Primitive Initialization ;+ ; Subroutine to clear the MSCP rings and perform primitive initialization. ;- BGTBUF: MOV #BBUFF-UMBOOT+B.MSIZ,R4 ;Point to buffer. 10$: CLR -(R4) ;Clear out the buffer. CMP R4,#BBUFF-UMBOOT ;Back to start of buffer yet?. BHI 10$ ;No, loop. MOV #B.MSIZ,-4(R4) ;Load command length. MOV (R0)+,P.OPCD(R4) ;Set up the correct opcode. MOV BUNIT,P.UNIT(R4) ;Get the unit number. RTS R0 .SBTTL BINLST - *BOOT* Bootstrap Impure Area, Boot Time CMD/MSG Rings ;+ ; ** WARNING: The following items must be kept in order. ;- BINLST: .BYTE 0 ;Init 1 Lo: no vector. .BYTE 0*10+0+STEP ;Hi: 2^0 msg ring, 2^0 cmd ring. .WORD BMRING-UMBOOT ;Init 2: message ring address <15:0>. .WORD 0 ;Init 3: message ring <17:16> (MBZ). .WORD GO ;Init 4: go. BLN: .WORD 0 ;Length of command/response. BVC: .WORD 0 ;Virtual circuit id. BBUFF: ;Message/command buff, 1 full packet. .SBTTL ALTCHK - *BOOT* Alternate CSR Check For FALCONs ;+ ; The following code allows booting of RT via DU on FALCONs. The reason ; for the special code is that on a FALCON, the address which would ; normally be the AIP CSR is actually part of the bootstrap ROM. ; ; The algorithm used is that if running on a FALCON, the alternate CSR ; is used. Otherwise, the sysgenned CSR address is used. Determination ; is made by using the MFPT instruction. ;- ALTCHK::MOV R0,-(SP) ;Save a work register MOV @#TRAP10,-(SP) ;Save current vector contents MOV PC,R0 ;Build address to our own routine ADD #BT.INS-.,R0 MOV R0,@#TRAP10 ;Redirect traps to our routine CLR R0 MFPT ;Get the processor type CMPB R0,#4 ;Are we running on a FALCON? BNE 999$ ;Nope... MOV #DU$ALT,BDUAIP ;Yes, so use mongrel "standard" 999$: MOV (SP)+,@#TRAP10 ;Restore saved vector MOV (SP)+,R0 ; and the work register RETURN BT.INS: RTI ; and return from trap PD.CSR: .WORD UM$CSR ;Port 0 IP .IF NE UM$PORTS-1 .WORD UM$CS1 ;Port 1 IP .IF NE UM$PORTS-2 .WORD UM$CS2 ;Port 2 IP .IF NE UM$PORTS-3 .WORD UM$CS3 ;Port 3 IP .ENDC ;NE UM$PORTS-3 .ENDC ;NE UM$PORTS-2 .ENDC ;NE UM$PORTS-1 .IF LT <.-> ;Reserve remainder of buffer. .=BBUFF+B.MSIZ .ENDC ;LT <.-> .WORD 0,0 ;Boot time interrupt identity area. BMRING: .WORD 0,0 ;Boot time message ring, 1 entry. BCRING: .WORD 0,0 ;Boot time command ring, 1 entry. .SBTTL PD.POR - *BOOT* Unit/Port Table ;+ ; Unit/Port table ;- ;only the first MAX$BT (=8.) units can be bootable PD.POR: $1=0 .REPT MAX$BT .IRP Y,\$1 .WORD UM$U'Y .BYTE UM$A'Y,UM$O'Y .ENDR $1=$1+1 .ENDR .SBTTL BOOT - *BOOT* Bootstrap SW BOOT Routine ;+ ; A good old software boot routine. It assumes the unit number in R0. ;- BOOT: MOV #10000,SP ;Set stack pointer. MOV R0,-(SP) ;Get the booted unit number. BIC #^C<7>,@SP ;Extract the unit number. MOV (SP),BUNIT ;Store for read routine. MOV #2,R0 ;Read in 2nd part from block 2. MOV #<4*400>,R1 ;Every block but us (4 blocks). MOV #1000,R2 ;Into location 1000. CLR (PC)+ ;Mark that this is a hardware boot. HRDBOT: .WORD 1 ;1 = a software boot. JSR PC,READ ;Go read it in. MOV #READ-UMBOOT,@#B$READ ;Store start location for read. MOV #B$DNAM,@#B$DEVN ;Store RAD50 device name. MOV (SP)+,@#B$DEVU ;Store the unit number. CALLR @#B$BOOT ;Start secondary boot. .DREND UM,PSECT=UMLCODE .SBTTL FALCON - Check If We Are On a Falcon ;+ ; FALCON ; ; What the code does is determine if the alternate "standard" CSR is ; to be used (for FALCON systems which have ROM at the normal standard ; address) and configures the handler such that it will use the appropriate ; addresses. ; ; This code is actually part of the FETCH/LOAD routine. It resides here ; rather than in the common UM module, because it is specific to DU. ;- .IF EQ MMG$T ;************************************************************************** .PSECT SETOVR ;************************************************************************** FALCON::.MFPS ;Save current PSW .MTPS #340 ;Don't let anything annoy us MOV R0,-(SP) ;Save R0 for awhile MOV @#TRAP10,-(SP) ;Save current trap vector contents .ADDR #UM.INS,R5 ;Build address of our own routine MOV R5,@#TRAP10 ;Redirect to our trap routine CLR R0 ;Reset for later test MFPT ;Do processor determination CMPB R0,#4 ;Running on a FALCON? BNE 10$ ;Nope, then no alteration needed MOV #DU$ALT,XUDAIP-UMBASE(R3) ;Yes, use the alternate CSR MOV #DU$ALT+2,XUDASA-UMBASE(R3) MOV #DU$ALT,XPCTAB-UMBASE(R3) MOV #DU$ALT+2,XPCTAB+2-UMBASE(R3) 10$: MOV (SP)+,@#TRAP10 ;Restore saved vector MOV (SP)+,R0 ;Restore saved work register .MTPS ;Restore previous priority RETURN ; In unmapped DU, the second block of psect SETOVR starting at OVRBK1 ; and the psect UMLST form the two blocks of load/fetch code read by ; the USR. UMLST gets padded out so that together the second block ; of SETOVR and psect UMLST end on a block boundary. Therefore, the ; psect UMBOOT will be aligned on a block boundary. LFEND:: LFSIZE = LFEND - OVRBK1 ;SIZE OF LOAD/FETCH CODE .PSECT UMLST .ASSUME LE <2 * BLK> . = UM.LST + <<2 * BLK> - LFSIZE> ;PAD UMLST. .IFF ;EQ MMG$T ;In order to to make LDOVR block aligned, we must pad the the ;PSECT which precedes it. .PSECT SETOV1 .ASSUME . LE LDBUF1+<1*BLK> . = LDBUF1+<1*BLK> .PSECT LDOVR .ASSUME . LE -UMLSZ> . = LDOVR+<1*BLK> - UMLSZ .ENDC ;EQ MMG$T .PSECT SPFUNS SFTAB: .DRSPF -,SF.ARD,TYPE=R ;377 Absolute Read I/O, + status bits .DRSPF -,SF.AWR,TYPE=W ;376 Absolute Write I/O, + status bits .DRSPF -,SF.R32,TYPE=R ;32-bit LBN Absolute Read .DRSPF -,SF.W32,TYPE=W ;32-bit LBN Absolute Write .WORD 0 .END