.MCALL .MODULE .MODULE DU,VERSION=63,COMMENT=,AUDIT=NO ; COPYRIGHT (c) 1984,1985, 1986 BY ; DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. ; ALL RIGHTS RESERVED. ; ; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED ; ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND 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. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY ; TRANSFERRED. ; ; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE ; AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT ; CORPORATION. ; ; DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS ; SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. .SBTTL CONDITIONAL ASSEMBLY SUMMARY ;+ ;COND ; DU$BOO (0) Multi-port booting support ; 0 no support ; 1 support ; ; DU$BBR (0) Dynamic Bad Block Replacement ; 0 no dynamic replacement ; 1 dynamic replacement (only if MMG$T=1) ; ; DU$ERL (0) Extended error logging ; 0 no extended error logging ; 1 extended error logging (only if MMG$T=1?) ; ; DU$UNIts(8.) Fixed at 8 (for now) ; ; DU$PORts(1) Supported ports ; 1-4 allowable range ; ; DU$NAM (^RDU ) Handler name for global region ; (and temporarily for booting) ; ; DU$CSR (172150) 0th CSR ; DU$ALT (176150) Alternate CSR (Falcon) ; DU$CS1 (DU$CSR-04) 1st CSR ; DU$CS2 (DU$CSR-10) 2nd CSR ; DU$CS3 (DU$CSR-14) 3rd CSR ; ; 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--7 (and 10--77 eventually) ; ; 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 ; ; MMG$T std conditional ; TIM$IT std conditional (no code effects) ; ERL$G std conditional ;- .SBTTL Functional description ;*Action* REVISE this section. ;+ ; 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 really seeks the ; unit vice the traditional RT-11 "just exit on seek". ; 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 By-pass recovery. ; o Bad Block replacement. ; o Error Logging full support. ; ; 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 372 ; Q$WCNT = + -> READ TABLE ; = - -> WRITE TABLE ; Special function code 372 returns the RT-11 to MSCP translation table. ; The buffer supplied by the user must point to a 18 word area to get ; the information. The format of the return area is: ; ; Offset Size ; .--------------------------------. ;Table ID | RAD50 "DU_" | 0 2 ; .--------------------------------. ;Unit count | 8(for now) | 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. / ;- ;+ ; Code 373 (VARSZ$) ; ; Special function code 373 returns the volume size as per standard RT11. ; ; ; ; Code 371 (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 ; in the user's job. The first 26 words are used to hold the response ; packet length (in bytes), the virtual circuit identifier and the end ; packet when the command is complete. The second 26 words must be set ; up by the caller to contain a length word, a virtual circuit ; identifier and a valid MSCP command. ; ; ** Warning: under XM, the 52 word control block must be in low memory. ; ; ----------------------------------------- ; 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 Design Overview ;+ ; 1. Overlay SET code ; 2. DUX - DUR - extended memory interface ($rel macro, p1ext) ; 3. Once only code (.DRPTR) ; 4. Once only code buffering. ; 5. Fetchable handler ; 6. Port structure .SBTTL CONDITIONALS .MCALL .ASSUME,.ADDR,.BR .NLIST CND ; Make sure some of the conditionals get defined/initialized. .IIF NDF DU$BOO, DU$BOO == 0 ; Make sure is off .IIF NDF DU$BBR, DU$BBR == 0 .IIF NDF ERL$G, ERL$G == 0 .IIF NDF DU$ERL, DU$ERL == 0 .IIF NDF MMG$T, MMG$T == 0 ; Number of RT-11 units supported .IIF NDF DU$INITS DU$UNITS = 8. ;Standard is 8 units .Assume DU$UNITS EQ 8., ; for now ; 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 EQ DU$PORTS-1, DU$BOO = 0 ;Never mult-port boot on one port ; For Host Initiated Bad Block Replacement we must be in XM. DU$BBR = DU$BBR & MMG$T ; Define global region name - default. .IIF NDF DU$NAM, DU$NAM = <^RDU > .IF EQ MMG$T B$DNAM =: DU$NAM .IFF B$DNAM =: DU$NAM+<^R X> .ENDC ; ; Define default SET UNIT/PART/PORT values .list me $1=0 ;loop counter .REPT DU$UNITS .IRP Y,\$1 .IIF NDF DU$U'Y DU$U'Y = $1 .IIF NDF DU$O'Y DU$O'Y = 0 .Assume DU$O'Y LE DU$PORTS .Assume DU$O'Y GE 0 .IIF NDF DU$A'Y DU$A'Y = 0 $1=$1+1 .ENDR .ENDR .nlist me ; Additional Audit .DUGEN =: DU$PORTS!! .DUUNI =: DU$UNI .AUDIT .DU,.DUGEN,.DUUNI .SBTTL MSCP PROTOCOL DEFINITIONS ;+ ; Command Packet Offsets ; ; Generic Command Packet Offsets and Field Lengths: ;- P.CRF =: 0 ;4 Command reference number P.UNIT =: 4 ;2 Unit Number ;2 Reserved P.OPCD =: 10 ;1 Opcode ;1 Reserved P.MOD =: 12 ;2 Modifiers P.BCNT =: 14 ;4 Byte count P.BUFF =: 20 ;12 Buffer descriptor P.LBN =: 34 ;4 Logical Block Number P.PART =: 36 ; RT-11 partition is high order LBN. P.CSIZ =: 60 ;48. Total length of a packet ;+ ; ABORT and GET COMMAND STATUS Command Packet Offsets and Field Lengths: ;- P.OTRF =: 14 ;4 Outstanding Reference number ;+ ; ONLINE and SET UNIT CHARACTERISTICS Command Packet Offsets and Field Lengths ;- P.UNFL =: 16 ;2 Unit flags P.DVPM =: 34 ;4 Device dependent parameters ;+ ; REPLACE Command Packet Offsets and Field Lengths: ;- P.RBN =: 14 ;4 Replacement block number ;+ ; SET CONTROLLER CHARACTERISTICS Command Packet Offsets and Field Lengths: ;- P.VRSN =: 14 ;2 MSCP version P.CNTF =: 16 ;2 Controller flags P.HTMO =: 20 ;2 Host timeout TO.MIN = 0 ; timeout (in minutes) P.TIME =: 24 ;8 Quad-word time and date ;+ ; End Packet Offsets ; ; Generic End Packet Offsets and Field Lengths: ;- P.CRF =: 0 ;4 Command reference number P.UNIT =: 4 ;2 Unit number ;2 Reserved P.OPCD =: 10 ;1 Opcode (also called endcode) P.FLGS =: 11 ;1 End message flags P.STS =: 12 ;2 Status P.BCNT =: 14 ;4 Byte count ;12 Reserved P.FBBK =: 34 ;4 First bad block ;+ ; ABORT and GET COMMAND STATUS End Packet Offsets and Field Lengths: ;- P.OTRF =: 14 ;4 Outstanding reference number ;+ ; GET COMMAND STATUS End Packet Offsets and Field Lengths: ;- P.CMST =: 20 ;4 Command status ;+ ; GET UNIT STATUS End Packet Offsets and Field Lengths: ;- P.MLUN =: 14 ;2 Multi-unit code P.UNFL =: 16 ;2 Unit flags P.UNTI =: 24 ;8 Unit identifier 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.MLUN =: 14 ;2 Multi-unit code P.UNFL =: 16 ;2 Unit flags P.UNTI =: 24 ;8 Unit identifer P.MEDI =: 34 ;4 Media type identifier P.UNSZ =: 44 ;4 Unit size P.VSER =: 50 ;4 Volume serial number ;+ ; SET CONTROLLER CHARACTERISTICS End Packet Offsets and Field Lengths: ;- P.VRSN =: 14 ;2 MSCP version P.CNTF =: 16 ;2 Controller flags P.CTMO =: 20 ;2 Controller timeout P.CNTI =: 24 ;8 Controller ID P.MSIZ =: 60 ;48. Total length of a packet ;+ ; Control Packet Opcodes ;- OP.NIL =: 0 ;NIL Command - USed internally with MSCP macro ; True opcodes OP.ABO =: 1 ;ABORT Command OP.GCS =: 2 ;GET COMMAND STATUS Command OP.GUS =: 3 ;GET UNIT STATUS Command OP.SCC =: 4 ;SET CONTROLLER CHARACTERISTICS Command OP.AVL =: 10 ;AVAILABLE Command OP.ONL =: 11 ;ONLINE Command OP.SUC =: 12 ;SET UNIT CHARACTERISTICS Command OP.DAP =: 13 ;DETERMINE ACCESS PATHS Command OP.ACC =: 20 ;ACCESS Command OP.ERS =: 22 ;ERASE Command OP.RPL =: 24 ;REPLACE Command OP.CMP =: 40 ;COMPARE HOST DATA Command OP.RD =: 41 ;READ Command OP.WR =: 42 ;WRITE Command OP.SEX =: 7 ;Serious Exception end message (see note below) OP.END =: 200 ;End packet flag (see note below) OP.AVA =: 100 ;AVAILABLE Attention Message OP.DUP =: 101 ;DUPLICATE UNIT NUMBER Attention Message OP.ACP =: 102 ;ACCESS PATH Attention Message ;+ ; 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.CMP =: 40000 ;Compare MD.EXP =: 100000 ;Express Request MD.ERR =: 10000 ;Force Error MD.SEC =: 1000 ;Suppress Error Correction MD.SER =: 400 ;Suppress Error Recovery ;+ ; AVAILABLE Command Modifiers: ;- MD.ALL =: 2 ;All Class Drivers 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 Errr ;+ ; 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 EF.LOG =: 40 ;Error Log Generated ;+ ; Unit Flags ;- UF.CMR =: 1 ;Compare Reads UF.CMW =: 2 ;Compare Writes UF.576 =: 4 ;576 Byte Sectors UF.RMV =: 200 ;Removable Media UF.WPH =: 20000 ;Write Protect (hardware) UF.WPS =: 10000 ;Write Protect (software) UF.BBR =: 100000 ;Controller Initiated BBR ;+ ; Controller Flags ;- CF.ATN =: 200 ;Enable Attention Messages CF.MSC =: 100 ;Enable Miscellaneous Error Log Messages CF.OTH =: 40 ;Enable Other Host's Error Log Messages CF.THS =: 20 ;Enable This Host's Error Log Messages CF.576 =: 1 ;576 Byte Sectors ;+ ; Status codes ;- ST.MSK =: 37 ;Mask for major status code ST.SUC =: 0 ;Success ST.INV =: 1 ;Invalid MSCP command ST.OFF =: 3 ;UNIT OFFLINE ST.AVL =: 4 ;Drive available ST.PRO =: 6 ;WRITE PROTECTED ST.DAT =: 10 ;DATA ERROR (SUB-CODE = FORCE ERROR) ST.DRV =: 13 ;DRIVE ERROR ;+ ; Error Log Message Packet Offsets and Field Lengths ;- L.CRF =: 0 ;4 Command reference number L.UNIT =: 4 ;2 Unit number L.SEQ =: 6 ;2 Sequence number L.FMT =: 10 ;1 Format L.FLGS =: 11 ;1 Error log message flags L.EVNT =: 12 ;2 Event code L.CNTI =: 14 ;8 Controller ID L.CSVR =: 24 ;1 Controller software version L.CHVR =: 25 ;1 Controller hardware version L.MLUN =: 26 ;2 Multi-unit code L.UNTI =: 30 ;8 Unit ID L.USVR =: 40 ;1 Unit software version L.UHVR =: 41 ;1 Unit hardware version ;2 Format dependent L.VSER =: 44 ;4 Volume serial number ;+ ; Error Log message Format Codes ;- FM.CNT =: 0 ;Controller errors FM.BAD =: 1 ;Host memory access errors with bus address FM.DSK =: 2 ;Disk transfer errors FM.SDI =: 3 ;SDI errors ;+ ; Error Log Message Flags ;- LF.SUC =: 200 ;Operation successful LF.CON =: 100 ;Operation continuing LF.SNR =: 1 ;Sequence number reset ;+ ; Host Memory Access Errors with Bus Address Error Log Message Offsets ;- L.BADR =: 4 ;4 Bus address ;+ ; Disk Transfer Errors Error Log Message Offsets ;- L.LVL =: 42 ;1 Level L.RTRY =: 43 ;1 Retry L.VSER =: 44 ;4 Volume serial number 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 UDA PORT DEFINITIONS ;+ ; Ownership flags. For Message Descriptor Vectors. ;- OWN =: 100000 ;UDA owns ring buffer entry FLAG =: 40000 ;UDA should interrupt on ring transition ;+ ; Initialization sequence definitions ;- ISTEP1 =: 4000 ;Initialization step 1 ISTEP2 =: 10000 ;Initialization step 2 ISTEP3 =: 20000 ;Initialization step 3 ISTEP4 =: 40000 ;Initialization step 4 IERROR =: 100000 ;Error IE =: 200 ;Interrupt during initialization sequence STEP =: 200 ;Mandatory one bit in step 1 (high byte) GO =: 1 ;GO at step 4 ;+ ; Port control table element offsets ;- PC.AIP =: 0 ;Polling register. PC.ASA =: 2 ;Status register. PC.STEP =: 4 ;Initialization step for this port. PC.VEC =: 6 ;Vector for this port. PC.ESZ =: 10 ;Port control table entry size. ;+ ; 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.SIZ =: 14 ; SIZE OF TABLE .SBTTL DRIVER DEFINITIONS ;+ ; Miscellaneous declarations ;- NRETRY =: 8. ;Retry initialization up to 8 times VT.ESZ =: 6 ;Size of RT-11 vector table in bytes. ILG.V =: 4 ;Illegal address trap INS.V =: 10 ;Illegal instruction trap vector KISAR1 =: 172342 ;PAR1 address BLK =: 1000 ;Size of a block in octal (512 bytes) BLK0 =: 0 ;Block zero 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 ; RT-11 Communication Area SYSPTR =: 54 ;System commnications area, pointer to base of RMON ;+ ; RMON Fixed offsets ;- MEMPTR =: 430 ;Fixed offset to memory pointers CORPTX =: 4 ; Offset to memory block FSIZE =: 0 ; Size 32 word units FADDR =: 2 ; Address/32 of region P1EXT =: 432 ;Pointer to PAR1 externalization routine CVAPHY =: -16 ;JMP to convert user virtual to physical address FINDGR =: -12 ;Find global region addr/32; use name XALLOC =: -6 ;Allocate memory from free list BLKMOV =: -2 ;BLOCK MOVE ROUTINE CONFG2 =: 370 ;Configuration word #2 QBUS$ =: 000100 ;Qbus hardware ;+ ; Global region control block ;- GR.SIZ =: 0 ; Size in 32 word blocks GR.ADR =: 2 ; Base address in 32-word blocks GR.STA =: 4 ; STatus byte GR.PVT =: 100000 ; Private region GR.NRF =: 000040 ; Don't return to free list GR.NAM =: 6 ; Name (2 rad50 words) GR.ESZ =: 10. ; Size of block ;+ ; PAR1 offsets ;- BEGREL =: 20000 ; BEGINNING OF PAR1 VIRTUAL ADDR ENDREL =: 37776 ; END OF PAR1 VIRTUAL ADDR PGSIZ =: 20000 ; PAGE SIZE IN BYTES ;+ ; Special function definitions ;- SP.RIO =: 377 ; ABSOLUTE READ IO SP.WIO =: 376 ; ABSOLUTE WRITE IO SP.VSZ =: 373 ;Special function to return unit size. SP.DAT =: 372 ;Return handler translation data. SP.BYP =: 371 ;Bypass class handler (job issued MSCP) SP.MUC =: 360 ;For compatibility with MU, 360 is also ; accepted here ;+ ; Absolute read/write error table ;- SPFUNB =: 100000 ; SUCCESS SPERR =: 077400 ; ERROR WAS NOT RECOVABLE SPFERR =: 040000 ; FORCED ERROR INDICATOR SPBBR =: 000200 ; BAD BLOCK DETECTED SPECC =: 000001 ; ECC CORRECTED ;+ ; Unit translation table offsets ;- UT.UNIT =: 0 ;MSCP unit number (2 bytes). UT.PART =: 2 ;Disk partition to be used (1 byte). UT.PORT =: 3 ;UDA port to be used (1 byte). UT.ESZ =: 4 ;Unit translation table entry size. .SBTTL DRDEF - definition ;+ ; Define RT-11 handler codes, vectors and CSRs. ;- .MCALL .DRDEF, .MFPS, .MTPS .DRDEF DU,50,FILST$!SPFUN$!VARSZ$,0,172150,154 .IF EQ MMG$T .DRPTR FETCH=ONCE,LOAD=ONCE .DREST CLASS=DVC.DK .IFF .DRPTR FETCH=ONCE,LOAD=ONCE .DREST CLASS=DVC.DK .ENDC ;EQ MMG$T .DRSPF SP.RIO ; ABSOLUTE READ IO .DRSPF SP.WIO ; ABSOLUTE WRITE IO .DRSPF SP.VSZ ;Special function to return unit size. .DRSPF SP.DAT ;Return handler translation data. .DRSPF ;Bypass class handler (job issued MSCP) ;For compatibility with MU, 360 is also ; accepted here .IIF NDF DU$ALT DU$ALT = 176150 ;Alternate "standard" CSR .IIF NDF DU$CS1, DU$CS1=DU$CSR-04 .IIF NDF DU$CS2, DU$CS2=DU$CSR-10 .IIF NDF DU$CS3, DU$CS3=DU$CSR-14 .IIF NDF DU$VC1, DU$VC1=DU$VEC-04 .IIF NDF DU$VC2, DU$VC2=DU$VEC-10 .IIF NDF DU$VC3, DU$VC3=DU$VEC-14 ;+ ; PSECT Ordering ;- .IF NE MMG$T .SAVE .PSECT DUDVR .PSECT SETOVR .PSECT DUBOOT .PSECT DUX DUXBAS:: .RESTORE .IFF .PSECT DUDVR .PSECT SETOVR .PSECT DUBOOT .ENDC ;NE MMG$T .SBTTL MACRO DEFINITIONS ;+ ; Create an MSCP command entry into the command table (by subroutine). ;- .MACRO MSCP OPCODE JSR R5,GETCBF .WORD OPCODE .ENDM ;+ ; Perform contiguous action while gone for interrupt. ;- .MACRO DO THIS,THEN,THAT JSR R5,COORD .WORD THAT-.,THIS-. .ENDM ; Push argument into the stack! .MACRO PUSH LIST .IRP XX, MOV XX,-(SP) .ENDM .ENDM PUSH ; Pop arguments from the stack! .MACRO POP LIST .IRP XX, MOV (SP)+,XX .ENDM .ENDM POP DUR.Cnt = 0 ; DU root from extended memory ref DUX.Cnt = 0 ; DU extended memory ref ;+ ; $REL ; ; Used to mark words to relocate in the handler ; ; $Rel Loc Value Base ; ; Loc -- location of word to relocate ; Value -- value to relocate it to ; Base -- base relocation on (DUR, DUX) ; ;- .Macro $Rel Loc Value Base .....1 = . . = Loc .If IDN DUX.Cnt = DUX.Cnt+1 .Irp x <\DUX.Cnt> DUX'x: .Word Value-DUXBase+BEGREL .EndR . = .....1 .MExit .EndC; IDN .If IDN DUR.Cnt = DUR.Cnt+1 .Irp x <\DUR.Cnt> DUR'x: .Word Value-DUBase .EndR . = .....1 .MExit .EndC; IDN .Error ; Unknown B A S E "Base"; .EndM $Rel ; PSECT MACROS .MACRO DUXPSECT .IF NE MMG$T .SAVE .PSECT DUX .ENDC ;NE MMG$T .ENDM DUXPSECT .MACRO DUPSECT .IIF NE MMG$T .RESTORE .ENDM DUPSECT ; P1ext Macros ;+ ; This macro is used to execute a set of instruction in P1ext - this ; instructions map the par1 space to another region. ;- ; LOW MEMORY .MACRO .P1EXT NEWPAR,?LOC MOV NEWPAR,LOC JSR R0,@$P1EXT .WORD LOC-. .MACRO .P1END LOC: .WORD 0 .ENDM .ENDM .P1EXT ; HIGH MEMORY .MACRO .HP1EX NEWPAR,?LOC MOV NEWPAR,LOC JSR R0,@H$P1EX .WORD LOC-. .MACRO .HP1EN LOC: .WORD 0 .ENDM .ENDM .HP1EX .SBTTL INSTALLATION CODE .ASECT . = 0 REF0:: ; Reference point zero . = 120 ;+ ; Installation code and EMT area for updating boot-time CSR ;- .IF EQ DU$PORTS-1 .DRINS DU .IFF .IF EQ DU$PORTS-2 .DRINS DU, .IFF .IF EQ DU$PORTS-3 .DRINS DU, .IFF .DRINS DU, .ENDC ;EQ DU$PORTS-3 .ENDC ;EQ DU$PORTS-2 .ENDC ;EQ DU$PORTS-1 BR DATAIN ; Data device entry BR SYSTIN ; System device entry; skip global DATAIN: .IF NE MMG$T ;+ TO prevent fetch/load error on data devices where there is no more ; memory. /just for nowdays/ MOV @#SYSPTR,R4 ; R4 = BASE OF RMON MOV MEMPTR(R4),R0 ; R0 = OFFSET TO MEMORY PTR ADD R4,R0 ; R0 -> TABLE OF MEMORY PTR MOV CORPTX(R0),R5 ; R5 = OFFSET TO FREE LIST ADD R4,R5 ; R5 -> FREE LIST 20$: CMP #-1,(R5)+ ; END OF LIST ? BNE 20$ ; NO, KEEP LOOKING FOR IT! ; R5 -> Start of region control block area 30$: CMP #-1,@R5 ; END OF RCB LIST ? BEQ I.BAD ; YES, BRANCH ; FAILURE TST @R5 ; Empty entry ? BEQ 40$ ; YES, BRANCH; FOUND AN ENTRY ADD #GR.ESZ,R5 ; POINT TO NEXT ENTRY BR 30$ ; REPEAT ; R5 -> Entry RCB 40$: MOV P1EXT(R4),R0 ; R0 -> EXTERN ROUTINES MOV #MEMDUX,R2 ; R2 = SIZE/32 MEMORY CALL XALLOC(R0) ; GET MEMORY FROM FREE LIST BCS I.BAD ; BRANCH IF NOT OBTAINED! ; Build RCB MOV R2,(R5)+ ; STORE THE SIZE/32 MOV R1,(R5)+ ; STORE THE ADDR/32 MOV #GR.PVT,(R5)+ ; STORE STATUS = PRIVATE MOV #DU$NAM,(R5)+ ; STORE NAME 1ST WORD MOV #<^R$>,@R5 ; STORE NAME 2ND WORD .DSABL LSB .ENDC ;NE MMG$T SYSTIN: .IF EQ DU$BOO ; Disable falcon if multi-port booting! CLR R0 ; Reset for later test MFPT ; Determine processor type CMP R0,#4 ; On a FALCON? BNE I.GOO ; Nope, we'll use normal CSR TST @#DU$ALT ; Yes, does alternate CSR exist? .IIF EQ MMG$T NOP ; (delay for SBC) BCS I.BAD ; Nope, reject installation .ENDC ;EQ DU$BOO ; Installation Common Exit Point I.GOO: TST (PC)+ I.BAD: SEC RTS PC .SBTTL SET code dispatch tables. ;+ ; The following routines will prepare for the call to the SET overlay ;- ;*note* Installation Area is borrowed for this code. .ENABL LSB OV.UT: MOV #SET.UT/2,R2 ; R2 = addr/2 to pass control BR 10$ ; MERGE OV.CSR: MOV #SET.CSR/2,R2 ; R2 = addr/2 to pass control BR 10$ ; MERGE OV.VEC: MOV #SET.VEC/2,R2 ; R2 = addr/2 to pass control BR 10$ ; MERGE OV.RET: MOV #SET.RET/2,R2 ; R2 = addr/2 to pass control 10$: BR GETOVR ; GO TO READ OVERLAY .DSABL LSB .ASSUME . LE 400 .SBTTL SET CODE TABLES ;+ ; Set definitions and tables. ;- .DRSET UNIT,UT.UNIT+1,XOV.UT,NUM ; The +1 is to avoid a table .DRSET PART,UT.PART+1,XOV.UT,NUM ; entry of 0 (table terminator). .DRSET PORT,UT.PORT+1,XOV.UT,NUM .DRSET CSR,<0*2+1>,XOV.CS,OCT .DRSET VECTOR,<0*2+1>,XOV.VE,OCT ; Optional SET looks .DRSET CSR0,<0*2+1>,XOV.CS,OCT .DRSET VEC0,<0*2+1>,XOV.VE,OCT .IF NE DU$PORTS-1 .DRSET CSR1,<1*2+1>,XOV.CS,OCT .DRSET VEC1,<1*2+1>,XOV.VE,OCT .IF NE DU$PORTS-2 .DRSET CSR2,<2*2+1>,XOV.CS,OCT .DRSET VEC2,<2*2+1>,XOV.VE,OCT .IF NE DU$PORTS-3 .DRSET CSR3,<3*2+1>,XOV.CS,OCT .DRSET VEC3,<3*2+1>,XOV.VE,OCT .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 .ENDC ;NE DU$PORTS-1 .DRSET RETRY,8.,XOV.RE,NUM .IF NE ERL$G .DRSET SUCCES,-1,OV.SUC,NO .ENDC ;NE ERL$G XOV.UT: BR OV.UT XOV.CS: BR OV.CSR XOV.VE: BR OV.VEC XOV.RE: BR OV.RET .IF NE ERL$G OV.SUC: CLR R2 ; 'YES' ENTRY BR ACNO ; SKIP NEXT OV.NO: MOV #4/2,R2 ; +4 /2 OF LOCATION ACNO: ADD #SET.SUC/2,R2 ; ADDRESS + 4 ('NO' ENTRY) BR GETOVR ; MERGE .ENDC ;NE ERL$G .ASSUME . LE <1000-144> ; Allocate room for GETOVR ; start! .SBTTL GETOVR - Get Overlay SET code routine from disk ;+ ; GETOVR - The following routine will read a disk overlay from disk ; containing the requested SET routine. It will also calculate ; the entry point in the overlay for the specific routine! ; ; Input : R2 = addr/2 of routine to enter ; ; NOTE: The Overlays will be read to block 0 - but not an entire block will ; be read! A routine will always be kept in block 0 to bring the real ; block zero into memory when the overlay has finished. ;- .ENABL LSB . = 1000 - 144 ; Don't slide down GETOVR: SWAB R2 ; GET BLK NUMBER INTO LOW BYTE MOVB R2,REABLK ; STORE THE BLK INTO EMT CLRB R2 ; CLEAR BLK, OFFSET IN HIGH ; BIS #BLK0/BLK,R2 ; USER BUFFER BLOCK FOR ADDR .ASSUME BLK0 EQ 0 JSR R0,10$ ; SAVE R0, R0 -> EMT REAFUN: .BYTE 17,10 ; CHANNEL , READ REABLK: .BLKW 1 ; BLOCK NUMBER TO READ REABUF: .BLKW 1 ; BUFFER ADDR TO READ .WORD VALWCT ; WORDS TO READ! .WORD 0 ; WAIT MODE I/O 10$: .ADDR #BLK0,R5,PUSH ; GET ADDR OF BUFFER MOV R5,REABUF ; SAVE INTO EMT MOV (SP)+,R5 ; RESTORE R5 ;**************************************************************************** VALWCT =: ./2 ; READ TILL HERE - PROTECT THE REST * OVRSIZ =: . ; USE IN .ASSUME IN OVERLAY CODE * ;**************************************************************************** EMT 375 ; ***.READW*** MOV (SP)+,R0 ; POP R0, SAVE CARRY BCS SET.NOR ; BRANCH IF ERROR SWAB R2 ; GET ADDR BACK ASL R2 ; MAKE INTO BYTE OFFSET .ADDR #BLK0,R2,ADD ; MAKE INTO REAL ADDRESS JMP @R2 ; GO TO ROUTINE .DSABL LSB .SBTTL GETBK0 - Read block zero - resident routine ;+ ; GETBK0 - This routine will not be overlayed by the SET code - instead ; it will always be called by the SET code routines to exit. ; The routine reads the real block zero from disk! ;- .ENABL LSB GETBK0: JSR R0,10$ ; SAVE R0 - POINT TO EMT BLOCK RDZERO: .BYTE 17,10 ; CHANNEL, READ .WORD BLK0 ; READ BLOCK ZERO FROM DISK RDBUF: .BLKW 1 ; BUFFER ADDRESS RDWC: .WORD VALWCT ; WORDCOUNT .WORD 0 ; WAIT I/O 10$: .ADDR #BLK0,R3 ; GET ADDRESS OF BUFFER MOV R3,RDBUF ; STORE INTO EMT BLOCK EMT 375 ; ***READW*** MOV (SP)+,R0 ; POP STACK, SAVE CARRY BCS SET.NOR ; EXIT IF ERROR! ; Common exit for everyone (including overlays) SET.EX: TST R2 ; ANY PREVIOUS ERRORS ? BEQ SET.NOR ; NO, BRANCH DEC R2 ; USUAL ERROR BEQ SET.ERR ; YES, BRANCH ADD #2,(SP) ; WRITE PROTECT ERROR - RETURN SET.ERR:SEC ; INDICATE ERROR SET.NOR:RTS PC ; RETURN .DSABL LSB SET.END: OSIZ ==: SET.END - GETOVR ; Size of fixed code! .ASSUME . LE 1000 .SBTTL DATA ALLOCATION .NLIST BEX .ENABLE LC,LSB ;+ ; Standard RT-11 entry point (BEGIN is logical entry point later in code). ;- ;********************************************************************* .DRBEG DU ;********************************************************************* DUBASE =: DUSTRT + 6 ; Relocation off $ENTRY BR ACROSS ; Skip next location! ;+ ; Global status storage location. (diagnostics usage) ;- STATU$::.WORD 0 ; OFFSET = DUBASE + 12 ;+ ; Begin executing ;- ACROSS: MOV DUCQE,R5 ; R5 -> CURRENT Q-ELEMENT .IF NE MMG$T MOV @#KISAR1,R1 ; STORE OLD MAPPING .P1EXT P1HIGH ; NEW MAPPING TO USE DUX JMP @#BEGIN ; GO TO IT $REL .-2 BEGIN DUX .P1END ; MARK END OF BLOCK .IFF JMP BEGIN ; This one jump saves a lot later. .ENDC ;NE MMG$T .DSABL LSB ;+ ; Vector table for multi-port configuration. ;- .IF NE DU$PORTS-1 .DRVTB DU,DU$VEC,DUINT .DRVTB ,DU$VC1,DUINT .IF NE DU$PORTS-2 .DRVTB ,DU$VC2,DUINT .IF NE DU$PORTS-3 .DRVTB ,DU$VC3,DUINT .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 .ENDC ;NE DU$PORTS-1 ;+ ; Impure area ***** THE FOLLOWING ITEMS MUST BE KEPT IN ORDER ***** ; An equivalent set of values will be defined in the DUX psect. Those ; values are going to be used when executing in high memory. The once ; only code .drptr will take into account the ordering of this values ; Impure area ***** THE FOLLOWING ITEMS MUST BE KEPT IN ORDER ***** ;- ;+ ; Active port values. ; Note that all items refer to the port currently being accessed. ;- UDAIP:: .WORD DU$CSR ; -> Address and polling register. UDASA:: .WORD DU$CSR+2 ; -> Status and Address register. ISTEP: .WORD 0 ; Next step bit, Done if <0. INISEQ: .WORD 0 ; -> in INILST to next item to load. INILST: .BYTE DU$VEC/4!IE ; INIT 1 LO: vector and IE for init. .BYTE 0*10+0+STEP ; INIT 1 HI: 2^0 both msg & cmd rings. MRPTR: .WORD 0 ; INIT 2: Message ring address <15:0> .WORD 0 ; INIT 3: Message ring address <21:16> .WORD GO ; INIT 4: go. ;+ ; Communication area ;- INTRID: .BLKW 2 ; Interrupt identity area. MRING: .WORD 0,0 ; Message ring, 1 entry. CRING: .WORD 0,0 ; Command ring, 1 entry. ;+ ; Miscellaneous data area ;- .IF NE ERL$G $SUCS: .WORD 0 ; Success log flag, 0 = YES. .ENDC ;NE ERL$G $RETRY: .WORD NRETRY ; Retry value (set code) .IF NE MMG$T ; Physical memory addresses PHYSH: .WORD 0 ; PHYSICAL HIGH 16-BITS PHYSL: .WORD 0 ; PHYSICAL LOW .ENDC ;NE MMG$T ;+ ; Unit translation table. ;- .UTTAB: .WORD 0 ; Pointer to current UTTAB entry. DUTAB: ; Beginning of table to SPFUN .RAD50 "DU " ; Table ID .Word DU$UNITS ; Table for "n" units UTTAB: .ASSUME UT.UNIT EQ 0 ; Initially, the RT-11 unit numbers .ASSUME UT.PART EQ 2 ; translate 1 to 1 with the MSCP unit .ASSUME UT.PORT EQ 3 ; numbers. The values may be set. $1=0 .REPT DU$UNITS .IRP Y,\$1 .WORD DU$U'Y .BYTE DU$A'Y .BYTE DU$O'Y $1=$1+1 .ENDR .ENDR ;+ ; Port control table. ;- .IF NE DU$PORTS-1 .PCTAB: .WORD 0 ; Pointer to current PCTAB entry. PCTAB: .ASSUME PC.AIP EQ 0 ; Initally, all port entries are set .ASSUME PC.ASA EQ 2 ; to 0. May be SET to other values. .ASSUME PC.STEP EQ 4 .ASSUME PC.VEC EQ 6 .WORD DU$CSR,DU$CSR+2 ; Port 0 (multi-port only) .WORD 0,DU$VEC/4!IE .WORD DU$CS1,DU$CS1+2 ; Port 1 data. .WORD 0,DU$VC1/4!IE .IF NE DU$PORTS-2 .WORD DU$CS2,DU$CS2+2 ; Port 2 data. .WORD 0,DU$VC2/4!IE .IF NE DU$PORTS-3 .WORD DU$CS3,DU$CS3+2 ; Port 3 data. .WORD 0,DU$VC3/4!IE ; End of multi-port table .ASSUME DU$PORTS LE 4 .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 .ENDC ;NE DU$PORTS-1 ; ESTABLISH SIZE OF LOW MEMORY DATA LOWDATA ==: . - UDAIP ; BYTES .ASSUME . LE DUSTRT+1000 ; SET CODE access range ; Impure************END OF CRITICAL ORDERING*********** ;********************************************************************* DUXPSECT ;********************************************************************* .IF NE MMG$T ;*****"Mirror Mirror on the wall whos' data is it off"**** XUDAIP::.WORD DU$CSR ; -> Address and polling register. XUDASA::.WORD DU$CSR+2 ; -> Status and Address register. XISTEP: .WORD 0 ; Next step bit, Done if <0. XINISE: .WORD 0 ; -> in INILST to next item to load. XINILS: .BYTE DU$VEC/4!IE ; INIT 1 LO: vector and IE for init. .BYTE 0*10+0+STEP ; INIT 1 HI: 2^0 both msg & cmd rings. XMRPTR: .WORD 0 ; INIT 2: Message ring address <15:0> .WORD 0 ; INIT 3: Message ring address <21:16> .WORD GO ; INIT 4: go. ;+ ; Communication area ;- XINTRI: .BLKW 2 ; Interrupt identity area. XMRING: .WORD 0,0 ; Message ring, 1 entry. XCRING: .WORD 0,0 ; Command ring, 1 entry. ;+ ; Miscellaneous data area ;- .IF NE ERL$G X$SUCS: .WORD 0 ; Success log flag, 0 = YES. .ENDC ;NE ERL$G X$RETR: .WORD NRETRY ; Retry value (set code) ; Physical memory addresses XPHYSH: .WORD 0 ; PHYSICAL HIGH 16-BITS XPHYSL: .WORD 0 ; PHYSICAL LOW ;+ ; Unit translation table. ;- X.UTTA: .WORD 0 ; Pointer to current UTTAB entry. XDUTAB: ; Beginning of table to SPFUN .RAD50 "DU " ; Table ID .WORD DU$UNITS ; Table for "n" units XUTTAB: $1=0 .REPT DU$UNITS .IRP Y,\$1 .WORD DU$U'Y .BYTE DU$A'Y .BYTE DU$O'Y $1=$1+1 .ENDR .ENDR ;+ ; Port control table. ;- .IF NE DU$PORTS-1 X.PCTA: .WORD 0 ; Pointer to current PCTAB entry. XPCTAB: .WORD DU$CSR,DU$CSR+2 ; Port 0 (multi-port only) .WORD 0,DU$VEC/4!IE .WORD DU$CS1,DU$CS1+2 ; Port 1 data. .WORD 0,DU$VC1/4!IE .IF NE DU$PORTS-2 .WORD DU$CS2,DU$CS2+2 ; Port 2 data. .WORD 0,DU$VC2/4!IE .IF NE DU$PORTS-3 .WORD DU$CS3,DU$CS3+2 ; Port 3 data. .WORD 0,DU$VC3/4!IE ; End of multi-port table .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 .ENDC ;NE DU$PORTS-1 ;************* "End of Reflection " ************* .ENDC ;NE MMG$T ;+ ; Command/Response buffer area ;- LN.CMD: .WORD P.CSIZ ; Length of command. VC.CMD: .WORD 0 ; Command virtual circuit ID. CBUFF: .BLKB P.CSIZ ; Command buffer: 1 full packet. LN.RSP: .WORD 0 ; Length of response. VC.RSP: .WORD 0 ; Response virtual circuit ID. MBUFF: .BLKB P.MSIZ ; Message buffer: 1 full packet. ;+ ; Miscellaneous data area ;- ;********************************************************************* DUPSECT ;********************************************************************* .IF NE MMG$T P1HIGH: .WORD 0 ; DUX MAPPING $P1EXT: .WORD 0 ; STORE P1EXT PTR HERE .ENDC ;NE MMG$T ;********************************************************************* DUXPSECT ;********************************************************************* RETRY: .WORD 0 ; Retry counter. VOLSIZE:.WORD 0,0 ; Size of current volume. NEXT: .WORD 0 ; Address of post interrupt activity. .IF NE MMG$T P1LOW: .WORD 0 ; STORE NEW MAPPING LOW HERE (DUR) ; Relocate some pointers locations .IF NE ERL$G H$ELPT: .WORD 0 ; ERROR LOGGING ENTRY .ENDC ;NE ERL$G H$PTWR: .WORD 0 ; PUTWORD PTR H$MPPT: .WORD 0 ; CONVERT VIRTUAL TO PHY H$GTBY: .WORD 0 ; GETBYTE PTR H$P1EX: .WORD 0 ; $P1EXT HIGH MEM PTR H$CQE: .WORD 0 ; DUCQE HIGM MEM PTR .ENDC ;NE MMG$T .IF NE DU$PORTS-1 ACTPORT:.WORD -1 ; Port which is in this data area now. .ENDC ;NE DU$PORTS-1 INTEOP: .WORD 0 ; INTERNAL OPERATION IN PROGRESS! .IF NE DU$BBR BBRON: .WORD 0 ; BBR IN PROGRESS FLAG .ENDC ;NE DU$BBR ;********************************************************************* DUPSECT ;********************************************************************* DUFBLK: .WORD 0,0,0,0 ; Fork queue block. .IF NE ERL$G ERLBUF: .BLKB P.MSIZ ; Error log end message LOW memory .ENDC ;NE ERL$G ;********************************************************************* DUXPSECT ;********************************************************************* .IF NE DU$BBR ;+ ; BBR Control Table ;- .IF NE DU$PORTS ACTBBR: .WORD 0 ; Pointer to current BBR entry BBRTAB: .ASSUME BB.STS EQ 0 ; Initally, all port entries are set .ASSUME BB.MAX EQ 2 ; to 0. May be SET to other values. .ASSUME BB.RCT EQ 6 .ASSUME BB.TRK EQ 10 .ASSUME BB.RBN EQ 12 .ASSUME BB.COP EQ 13 .WORD 0 ; PORT 0 STATUS WORD .BLKW 5 .IF NE DU$PORTS-1 .WORD 0 ; PORT 1 STATUS WORD .BLKW 5 .IF NE DU$PORTS-2 .WORD 0 ; PORT 2 STATUS WORD .BLKW 5 .IF NE DU$PORTS-3 .WORD 0 ; PORT 3 STATUS WORD .BLKW 5 .ASSUME DU$PORTS LE 4 .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 .ENDC ;NE DU$PORTS-1 .ENDC ;NE DU$PORTS .SBTTL BBR data structures and definitions ;+ ; DATA AREA - BBR ;- LBN: .BLKW 400 ; BUFFER FOR DATA FROM BAD LBN SEC0: .BLKW 400 ; 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 400 ; FIRST BUFFER FOR REPLACEMENT CONTROL TABLE RCTB1: .BLKW 400 ; SECOND BUFFER FOR REPLACEMENT CONTROL TABLE ;+ ; I/O PARAMETERS FOR BBR ;- XFRSZ: .BLKW 1 ; TRANSFER SIZE ; *** NOTE *** ; DO NOT CHANGE THE ORDER OF THE FOLLOWING 2 LINES (HIGH WORD MUST COME 1ST) 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 ;+ ; 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 ;- OOPS: .BLKW 1 ; FLAG WORD! 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 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 DESCRIPTO OLDSTA: .BLKW 1 ; OLD STATUS FOR OPERATION WRKSIZ ==: OLDSTA+2-XFRSZ ; SIZE IN WORDS OF THE WORK AREA! .ENDC ;NE DU$BBR .SBTTL ********************************** .SBTTL EXECUTABLE CODE BEGINS * .SBTTL ********************************** ;+ ; The RT-11 unit in the queue element is used to find the corresponding ; mspc unit and port. The data structures which belong to this unit are ; loaded into the 'active' set, (data structures = port values, bbr values). ; Some flags are initialized and if the function is to get the translation ; table we jump to the corresponding routine inmediatly. ;- BEGIN:: .IF EQ MMG$T MOV $RETRY,RETRY ; Initialize error retry count. .IFF MOV 2(SP),SP ; RESTORE STACK ADD #6,SP ; POP RETURN ARGUMENTS MOV R5,H$CQE ; STORE Q-ELMENT PTR HERE MOV R1,P1LOW ; STORE LOW MAPPING MOV X$RETRY,RETRY ; INITIALIZE ERROR RETRY COUNT .ENDC ;EQ MMG$T CLR INTEOP ; INITIALIZE INTERNAL OPERATION FLAG .IF NE DU$BBR CLR BBRON ; CLEAR BBR IN PROGRESS FLAG .ENDC ;NE DU$BBR CMPB Q$FUNC(R5),#SP.DAT ; Is this a table .SPFUN (372) BNE 10$ ; No, then go set up the port. JMP DOTAB ; Yes, then go read or write table 10$: MOVB Q$UNIT(R5),R3 ; Get the unit number. BIC #^C<7>,R3 ; Isolate the unit number. ASL R3 ; Create an index into the RT-11 ASL R3 ; to MSCP unit translation table. .ASSUME UT.ESZ EQ 4 .IF EQ MMG$T .ADDR #UTTAB,R3,ADD ; R3 -> UNIT TRANSLATION MOV R3,.UTTAB ; Save R3 for later. .IFF ADD #XUTTAB,R3 ; R3 -> UNIT TRANSLATION $REL .-2 XUTTAB DUX MOV R3,X.UTTAB ; SAVE R3 FOR LATER .ENDC ;EQ MMG$T .IF NE DU$PORTS-1 MOVB UT.PORT(R3),R2 ; R2 = Port number. BPL 30$ ; BRANCH IF IT IS OK! JMP DUHERR ; INVALID PORT; EXIT 30$: MOV R2,R3 ; Save for later in R3. ASL R2 ; Create index into the port control ASL R2 ; table by shifting left. ASL R2 .ASSUME PC.ESZ EQ 10 .IF EQ MMG$T .ADDR #PCTAB,R2,ADD ; R2 -> PORT CONTROL TABLE ENTRY MOV R2,.PCTAB ; Save pointer for later. .IFF ADD #XPCTAB,R2 ; R2 -> PORT CONTROL TABLE ENTRY $REL .-2 XPCTAB DUX MOV R2,X.PCTAB ; Save pointer for later. .ENDC ;EQ MMG$T CMP R3,ACTPORT ; Is this the currently active one? BEQ NOSW ; Yes, don't context switch new port. MOV R3,ACTPORT ; Record who's context this is. .IF EQ MMG$T .ADDR #UDAIP,R4 ; R4 -> IMPURE AREA .IFF MOV #XUDAIP,R4 ; R4 ->IMPURE TABLE $REL .-2 XUDAIP DUX .ENDC ;EQ MMG$T MOV (R2)+,(R4)+ ; Get the CSR, it is the IAP. .ASSUME PC.AIP EQ 0 MOV (R2)+,(R4)+ ; Get the ASA. .ASSUME PC.ASA EQ 2 MOV (R2)+,(R4)+ ; Get the new ports init status. .ASSUME PC.STEP EQ 4 .IF EQ MMG$T MOVB (R2),INILST ; Get the new ports vector code. .ASSUME PC.VEC EQ 6 .IFF MOVB (R2),XINILST ; GET THE NEW PORTS VECTOR CODE .ENDC ;EQ MMG$T .ENDC ;DU$PORTS-1 NOSW: .IF NE DU$BBR .IF NE DU$PORTS-1 MOV ACTPORT,R1 ; R1 = ACTIVE PORT .IFF CLR R1 ; R1 = ONLY PORT ZERO .ENDC ;NE DU$PORTS-1 ASL R1 ; *2 ASL R1 ; *4 MOV R1,-(SP) ; SAVE IN STACK ASL R1 ; *8 ASL R1 ; *16 SUB (SP)+,R1 ; -(PORT*4) => OFFSET TO ENTRY .ASSUME BB.SIZ EQ 14 ADD #BBRTAB,R1 ; R1 -> BBR TABLE ENTRY $REL .-2 BBRTAB DUX MOV R1,ACTBBR ; STORE THE ACTIVE ENTRY .ENDC ;NE DU$BBR .BR START .SBTTL PORT INITIALIZATION CODE ;+ ; The code checks the port status - if either reset or errored - the ; port is initialized using the four step process defined by the uq ; port spec. During initialization the controller is given information ; about the size of the command/response rings, the locations of two ; vectors which point to the rings and the interrupt id area. ; Each step generates interrupts if desired, the interrupt code will ; be flagged to return here for continuing until the four step process ; is completed. ; - .ENABL LSB START: CLR R5 ; Clear R5 for init just in case. .IF EQ MMG$T TST ISTEP ; Is this port fully initialized? .IFF TST XISTEP ; IS THIS PORT FULLY INITIALIZED ? .ENDC ;EQ MMG$T BPL INIT ; No, then go init it. .IF EQ MMG$T BIT #,@UDASA ; Has port errored or been reset? .IFF MOV #XUDASA+2,R5 ; R5 -> UDASA+2 $REL .-2 XUDASA+2 DUX BIT #,@-(R5) ; HAS PORT ERROR OR BEEN RESET ? .ENDC ;EQ MMG$T BNE INIT ; YES, so must NOT be running JMP DISPAT ; GO DISPATCH INIT: MOV #,INITFL ; Set the initialization flag ; (used to indicate that we must ; set the 'host timeout' for this ; port after the init completes ; and put the unit online.) DEC RETRY ; Have we tried long enough? BGE 10$ ; No, then do an initialization. HARDEX: JMP DUHERR ; Yes, take the hard error exit. 10$: .IF NE MMG$T MOV #XUDAIP,R5 ; R5 -> UDAIP ADDRESS $REL .-2 XUDAIP DUX MOV R5,@(R5) ; STROBE THE UDA TO START INIT MOV #XISTEP,R4 ; R4 -> INIT PARAMETERS $REL .-2 XISTEP DUX .IFF MOV R5,@UDAIP ; Strobe the UDA to start the init. .ADDR #ISTEP,R4 ; R4 -> initialization parameters. .ENDC ;NE MMG$T MOV #ISTEP1,(R4)+ ; Set step bit for step 1. MOV R4,@R4 ; Point INISEQ at INILST-2. .IF EQ MMG$T ;*NOTE* The code below is omitted in XM because the 22-bit value is ; calculated in the .drptr code and stored. CMP (R4)+,(R4)+ ; Point to message ring pointer. MOV R4,@R4 ; During init, send port the address ADD #MRING-MRPTR,@R4 ; of the message ring. .ENDC ;EQ MMG$T INISTP: .IF EQ MMG$T .ADDR #UDASA,R4 ; R4 -> -> UDASA REGISTER .IFF MOV #XUDASA,R4 ; GET THE ADDR OF ASA ADDRESS $REL .-2 XUDASA DUX .ENDC ;EQ MMG$T MOV @(R4)+,R5 ; Get the contents of the ASA. BMI INIT ; Try again on error. BIT R5,@R4 ; Is the desired step bit on? BEQ INISTP ; If not wait for it. ASL (R4)+ ; Set next step for next time. ADD #2,(R4)+ ; Advance to parameters to load. .IF NE MMG$T MOV #XUDASA+2,R5 ; R5 -> UDASA+2 $REL .-2 XUDASA+2 DUX MOV @-(R4),@-(R5) ; SET THE NEXT WORD .IFF MOV @-(R4),@UDASA ; Set the next word (*INTEN*) .ENDC ;NE MMG$T TST -(R4) ; Which step is this? .ASSUME ISTEP4*2 EQ 100000 .IF EQ DU$PORTS-1 BMI START ; Step 4 so go start the transfer. .IFF BPL 1$ ; Not step 4 go RTS to wait for int. .IF NE MMG$T MOV X.PCTAB,R5 ; Yes so get address of CP entry. .IFF MOV .PCTAB,R5 ; Yes so get address of CP entry. .ENDC ;NE MMG$T MOV (R4),PC.STEP(R5) ; Save init state in port impure area. BR START ; Go try to start the transfer now. .ENDC ;NE DU$PORTS-1 1$: .IF NE MMG$T HRET: .HP1EX P1LOW ; MAP TO LOW JMP @#LRET1 ; RETURN TO LOW MEMORY EXIT $REL .-2 LRET1 DUR .HP1EN ; MARK END OF BLOCK ;********************************************************************* DUPSECT ;********************************************************************* LRET1: MOV 2(SP),SP ; RESTORE STACK ADD #6,SP ; POP SOME ARGUMENTS .ENDC ;NE MMG$T RTS PC ; Wait for an interrupt, back at DUINT .DSABL LSB .SBTTL FUNCTION DISPATCH AREA ;+ ; The controller characteristics are set if we just completed a four step ; initialize and the required unit is brought online. In the case of ; bbr the online is followed by a get status to obtain parameters needed ; in the bbr algorithm. All the above operations used the interrupt facility ; and then return back to this point. ; The function byte is obtained from the q-element - the operation requested ; is decoded and control is passed to the appropiatte routine. ;- .ENABL LSB ;********************************************************************* DUXPSECT ;********************************************************************* DISPAT: ASLB (PC)+ ; Do we have to set timeout value? .ASSUME IN.TMO EQ 200 INITFL: .WORD 0 ; : Init flag (timeout & unit online) BCC 4$ ; Branch if not DO SETTMO,THEN,4$ ; Set the timeout, then check ONLINE 4$: .IF EQ MMG$T MOV DUCQE,R5 ; R5 -> current queue element. .IFF MOV H$CQE,R5 ; R5 -> CURRENT QUEUE ELEMENT .ENDC ;EQ MMG$T TST (R5)+ ; R5 -> .SPFUN code in queue element .ASSUME Q$FUNC EQ 2 CMPB @R5,#SP.MUC ; Is it the MU compatible bypass? BEQ 7$ ; Branch if yes CMPB @R5,#SP.BYP ; Is this the bypass .SPFUN? BNE 8$ ; Branch if not 7$: MOVB #SP.MUC,@R5 ; Make bypass MU compatible CMP #1,-(R5) ; Does user want bypass recovery? .ASSUME Q$BLKN EQ 0 BNE 5$ ; Branch if not 8$: ASL INITFL ; Do we need to put unit on-line? .ASSUME IN.ONL EQ 100000 BCC 5$ ; Branch if not BRINON: .IF EQ DU$BBR DO TRYONL,THEN,DISPAT ; GO DO AN INTERNAL ONLINE .IFF DO TRYONL,THEN,GETUS ; IF HOST BBR - DO ONL + GUS .ENDC ;EQ DU$BBR 5$: .IF EQ MMG$T MOV DUCQE,R5 ; R5 -> current queue element. .IFF MOV H$CQE,R5 ; R5 -> CURRENT QUEUE ELEMENT .ENDC ;EQ MMG$T MOVB Q$FUNC(R5),R4 ; R4 = function to perform. BNE 10$ ; Is it a read/write or seek? .IF EQ ERL$G .IF NE MMG$T CALL BUSCHK ; Go check bus limits on dma BCS HARDEX ; Branch if error .ENDC ;NE MMG$T DO IOXFER,THEN,DUEXIT ; Yes, do I/O transfer then exit. .IFF .IF NE MMG$T CALL BUSCHK ; Go check bus limits on dma BCS HARDEX ; Branch if error .ENDC ;NE MMG$T DO IOXFER,THEN,LOGIT ; Yes, do I/O and log success. .ENDC ;EQ ERL$G 10$: CMPB R4,#SP.MUC ; Is this the bypass .SPFUN? BNE 20$ ; Branch if not; check next function. DO BYPASS,THEN,DUEXIT ; Yes, do bypass action and exit. 20$: CMPB R4,#SP.VSZ ; Is this the volume sizer? BNE 45$ ; NO, BRANCH DO ONLINE,THEN,GETSIZ ; Do the Online request to getsize 45$: CMPB R4,#SP.WIO ; ABSOLUTE WRITE ? BLT NOFUN ; NO, BRANCH BHI 50$ ; IS A READ, SKIP NEXT NEG Q$WCNT(R5) ; FAKE A WRITE...TO IOXFER 50$: MOV #SPFUNB,(PC)+ ; INDICATE SPECIAL READ/WRITE SPCIO: .WORD 0 ; STORE HERE AS FLAG .IF NE MMG$T MOV Q$PAR(R5),(PC)+ ; STORE PAR VALUE FOR LATER SPPAR: .WORD 0 ; HERE... .ENDC ;NE MMG$T MOV Q$BUFF(R5),(PC)+ ; STORE BUFF ADDR TO RETURN STATUS SPBUFA: .WORD 0 ; HERE... ;*Action* Is this check really needed ? I later use physical addresses ! ADD #2,Q$BUFF(R5) ; ADJUST START OF DATE BUFFER .IF NE MMG$T CMP Q$BUFF(R5),#40000 ; OVERFLOW THIS PAGE ? BLO 55$ ; NO, BRANCH SUB #20000,Q$BUFF(R5) ; YES, REDUCE TO RANGE 20000-37777 ADD #200,Q$PAR(R5) ; AND BUMP PAGE ADDRESS 55$: CALL BUSCHK ; Go check bus limits on dma BCS HARDEX ; Branch if error .ENDC ;NE MMG$T DO IOXFER,THEN,DUEXIT ; GO DO IT! NOFUN: JMP DUEXIT ; No, then its an invalid function ; and we will ignore it HINIST: BR INISTP ; 'HELP - I NEED SOMEBODYS' HELP' ; -the Beatles .DSABL LSB .SBTTL COORD - Coordination routine for handler ;+ ; Pseudo subroutine to coordinate pre and post interrupt activity. ;- COORD: MOV R5,R4 ; Get address of routine offset. ADD (R5)+,R4 ; Get address of routine after int. MOV R4,NEXT ; Store if for after interrupt. MOV R5,R4 ; Reset R4. ADD (R5)+,R4 ; Get address of pre-int. routine. MOV (SP)+,R5 ; Tidy the stack / restore R5 JSR PC,@R4 ; Go do the setup routine. .BR POLL ; Now start the activity. .SBTTL POLL - This routine starts the controller! ;+ ; Send the command buffer to the port (ie. start the polling). ;- POLL: .IF EQ MMG$T BIS #OWN,CRING+2 ; Give the command to start polling. MOV @UDAIP,R5 ; Read the AIP to start it. BIS #,MRING+2 ; Give the port a reply buffer. .IFF BIS #OWN,XCRING+2 ; GIVE THE COMMAND TO START POLLING MOV #XUDAIP,R5 ; R5 -> UDAIP $REL .-2 XUDAIP DUX MOV @(R5),R5 ; READ THE AIP TO START IT BIS #,XMRING+2 ; Give the port a reply buffer. .ENDC ;EQ MMG$T .IF EQ MMG$T RTS PC ; Return, we'll be back on interrupt. .IFF JMP HRET ; GO RETURN TO LOW BEFORE RTS .ENDC ;EQ MMG$T .SBTTL INTERRUPT ENTRY POINT ;+ ; Interrupt and abort entry points ; ; Output: R5 -> Response buffer ;- .ENABL LSB ;********************************************************************* DUPSECT ;********************************************************************* .DRAST DU,5 ; Set up the interrupt entry. .IF NE MMG$T .P1EXT P1HIGH ; MAP TO HIGH MEMORY CALL @#INTEX ; ENTER INTERRUPT IN XM $REL .-2 INTEX DUX .P1END JMP FORKIT ; GO FORK IN LOW MEMORY .ENDC ;NE MMG$T ;********************************************************************* DUXPSECT ;********************************************************************* .IF NE MMG$T INTEX: CLR XINTRID ; Ack interrupt(s) CLR XINTRID+2 ; ... RETURN ;******************************************************************** DUPSECT ;******************************************************************** FORKIT: .FORK DUFBLK ; Well fork queue buddy! MOV @#KISAR1,R1 ; STORE OLD MAPPING .P1EXT P1HIGH ; MAP TO HIGH MEMORY JMP @#INTEX2 ; ENTER INTERRUPT IN XM $REL .-2 INTEX2 DUX .P1END ;******************************************************************** DUXPSECT ;******************************************************************** INTEX2: MOV 2(SP),SP ; RESTORE STACK POINTER ADD #6,SP ; POP SOME ARGUMENTS OFF MOV R1,P1LOW ; STORE LOW MAPPING TST XISTEP ; Are we initializing the port? .IFF CLR INTRID ; ACK INTERRUPT(S) CLR INTRID+2 ; ... .FORK DUFBLK ; WELL FORK QUEUE BUDDY! TST ISTEP ; Are we initializing the port? .ENDC ;NE MMG$T BGT HINIST ; YES, BRANCH .IF EQ MMG$T BIT #,@UDASA ; Has port errored or been reset? .IFF MOV #XUDASA+2,R5 ; R5 -> UDASA+2 $REL .-2 XUDASA+2 DUX BIT #,@-(R5) ; HAS PORT ERRORED OR BEEN RESET? .ENDC ; EQ MMG$T BEQ 47$ ; NO, SKIP NEXT JMP INIT ; Yes, then we must re-initialize it 47$: .IF NE DU$BBR TST BBRON ; IS BBR IN PROGRESS ? BPL 60$ ; NO, BRANCH JMP CONBBR ; GO - GET BACK INTO BBR .ENDC ;NE DU$BBR 60$: TST INTEOP ; IS THIS AN INTERNAL OPERATION? BMI 44$ ; Branch if internal with no recovery BNE 10$ ; Branch if internal with recovery 50$: .IF EQ MMG$T MOV DUCQE,R5 ; R5 -> current queue element. .IFF MOV H$CQE,R5 ; R5 -> CURRENT QUEUE ELEMENT .ENDC ;EQ MMG$T CMPB #SP.MUC,Q$FUNC(R5) ; The "bypass" special function? BNE 10$ ; No, then carry on. CMP #1,(R5) ; Does he wants by-pass recovery? BNE 44$ ; Branch if not 30$: ; The following code is needed because MRING for by-pass and regular ; commands is different in XM. .IF NE MMG$T MOV Q$PAR(R5),R1 ; GET USER MAPPING MOV Q$BUFF(R5),R5 ; R5 -> START OF CMD/RES BUFFER ADD #<4>,R5 ; R5 -> BEGINNING OF RESPONSE .HP1EX R1 ; START OF P1EXT BLOCK MOV P.STS(R5),R1 ; GET STATUS MOV R1,@#STATU$ ; STORE STATUS FOR CRASH... $REL .-2 STATU$ DUR MOVB P.FLGS(R5),R2 ; R2 = End message flags .HP1EN ; END OF BLOCK BR 40$ ; GO MERGE .ENDC ;NE MMG$T 10$: .IF NE MMG$T MOV #MBUFF,R5 ; R5 -> RESPONSE BUFFER $REL .-2 MBUFF DUX .IFF MOV MRING,R5 ; Point to message buffer. .IFTF MOV P.STS(R5),R1 ; R1 = STATUS .IFF MOV R1,STATU$ ; STORE STATUS FOR CRASH... .IFT .HP1EX P1LOW ; START OF P1EXT BLOCK MOV R1,@#STATU$ ; STORE STATUS 'RELOC' $REL .-2 STATU$ DUR .HP1EN ; END OF BLOCK .IFTF CLR R2 ; CLEAR TO PREVENT SIGN EXTEND BISB P.FLGS(R5),R2 ; R2 = End message flags .ENDC ;NE MMG$T 40$: .IF NE DU$BBR BITB R2,#EF.BBR ; BAD BLOCK REPORTED ? BEQ NOBBR ; NO, BRANCH JMP BBFND ; GO DO BAD BLOCK REPLACEMENT NOBBR: ; ENTRY POINT FOR 'CANNOT DO BBR' .ENDC ;NE DU$BBR BIC #^C,R1 ; Isolate major status code bits. .ASSUME ST.SUC EQ 0 TST R1 ; did the I/O or function go ok? .ASSUME ST.SUC EQ 0 BNE 20$ ; No, then try "online". 44$: CLR INTEOP ; RESET INTERNAL OP FLAG JMP @NEXT ; Yes, then do post int. activity. 20$: CMP R1,#ST.AVL ; Was it not available? .IF EQ ERL$G BNE DUHERR ; NO, so not recoverable. .IFF BEQ 48$ ; YES, TRY ONLINE JMP BADIO ; NO, Log the bad I/O ,take hard exit. 48$: .ENDC ;EQ ERL$G HSTART: .IIF NE ERL$G, CALL ERRLOG ; Log the error. DEC RETRY ; No, then have we tried too much. BLE DUHERR ; Yes,then its a hard error (init). JMP BRINON ; Attemp to bring unit online .DSABL LSB .SBTTL NORMAL AND STANDARD EXITS ;+ ; Exit to RT-11 with error flag set. ;- DUHERR: TST SPCIO ; ARE WE DOING ABSOLUTE R/W? BEQ 10$ ; NO, BRANCH CALL SPCERR ; GO PUT ERROR WORD BEFORE BYE... 10$: .IF EQ MMG$T MOV DUCQE,R5 ; R5 -> current queue element. .IFF MOV H$CQE,R5 ; R5 -> CURRENT QUEUE ELEMENT .ENDC ;EQ MMG$T BIS #HDERR$,@-(R5) ; Set the hard error bit. .BR DUEXIT ; Return to RT-11. ;+ ; Standard exit to RT-11 ;- .ENABL LSB DUEXIT: TST SPCIO ; ARE WE DOINT ABSOLUTE R/W ? BEQ 10$ ; NO, BRANCH CALL SPCEXI ; GO PUT SUCCESS WORD BEFORE BYE... 10$: .IF NE MMG$T .HP1EX P1LOW ; MAP TO LOW JMP @#LRET2 ; RETURN TO LOW DRFIN EXIT $REL .-2 LRET2 DUR .HP1EN ; MARK END OF BLOCK ;********************************************************************* DUPSECT ;********************************************************************* LRET2: MOV 2(SP),SP ; FIX STACK ADD #6,SP ; POP SOME ARGUMENTS .ENDC ;NE MMG$T .DRFIN DU ; Macro, macro what do you do? .DSABL LSB .SBTTL SPCEXI - SPECIAL ABSOLUTE READ/WRITE EXITS ;+ ; 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 ;********************************************************************* DUXPSECT ;********************************************************************* SPCERR: BITB #EF.BBR,R2 ; WAS IT A BAD BLOCK ? BEQ 20$ ; NO, BRANCH BIS #SPBBR,SPCIO ; OR THE BAD BLOCK FLAG ! BR SPCEXI ; GO MERGE 20$: CMPB #ST.DAT,R1 ; WAS IT A FORCED ERROR ? BNE 10$ ; NO, BRANCH BIS #SPFERR,SPCIO ; OR THE FORCED ERROR FLAG BR SPCEXI ; GO MERGE 10$: BIS #SPERR,SPCIO ; OR THE OTHER ERROR FLAG SPCEXI: MOV SPBUFA,R2 ; R2 -> USER Q$BUFF .IF NE MMG$T MOV H$CQE,R4 ; R4 -> CURRENT QUEUE ELEMENT MOV R2,Q$BUFF(R4) ; STORE BUFF IN Q-ELEMENT MOV SPPAR,Q$PAR(R4) ; RESTORE MAPPING MOV SPCIO,-(SP) ; STORE WORD TO PUT CALL @H$PTWR ; PUT STATUS IN USER BUFFER .IFF MOV SPCIO,(R2) ; STORE IN FB USER BUFFER .ENDC ;NE MMG$T CLR SPCIO ; RESET FLAG RTS PC ; RETURN TOCALL .DSABL LSB .SBTTL SETTMO - Sets 'host timeout' period ;+ ; ; SETTMO ; This routine is called as a result of a controller initialize ; (on-line) procedure and is used to set the controller 'host ; timeout' period before the actual transfer begins. ; ;- SETTMO: MOVB #IOP.NR,INTEOP ; INDICATE INTERNAL OP (NO RECOVERY) MSCP OP.SCC ; 'SET CONTROLLER CHARACTERISTICS' MOV #TO.MIN*60.,P.HTMO(R4) ; Set the value RTS PC .SBTTL ONLINE - Do online request for retry or sizing ;+ ; The following routine will provide an online of the desired unit. ; Two entry points are available, one for internal operations which ; desire to bring the unit up, the second entry is used to get the ; size of the drive indicated by the selected unit. ;- TRYONL: MOV #IOP.NR,INTEOP ; INTERNAL OP (NO RECOVERY) ONLINE: MSCP OP.ONL ; Select the online command. RTS PC .IF NE DU$BBR .SBTTL IGETUS - Do a GET UNIT STATUS request ;+ ; The get unit status is used to obtain information about the unit, write ; protect and it also returns parameters used for the bad block replacement. ;- IGETUS: MOV #IOP.NR,INTEOP ; Indicate INTERNAL op (no recovery) MSCP OP.GUS ; SELECT GET UNIT STATUS COMMAND. RTS PC ; RETURN! .ENDC ;NE DU$BBR .SBTTL SUBROUTINE TO INITIALIZE FOR I/O TRANSFERS ;+ ; This subroutine sets up the MSCP command buffer for I/O transfers. ; ; Input: R5 -> $QBLKN(QUEUE ELEMENT) ; R4 -> COMMAND PACKET (PAR1 OFFSET IN XM) ;- IOXFER: MSCP OP.RD ; Create an MSCP read. MOV (R5),R1 ; Save logical block number in R1. MOV R1,P.LBN(R4) ; Get the logical block number. .IF EQ MMG$T MOV Q$BUFF(R5),P.BUFF(R4) ; Get the user's buffer address. .IFF 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 !*! 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 ; ABSOLUTE READ/WRITE ? BEQ 30$ ; NO, BRANCH SUB #2,Q$BUFF(R5) ; UPDATE BUFFER ADDRESS Q-ELEMENT 30$: MOV Q$WCNT(R5),R0 ; Get the word count. BPL 1$ ; 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 1$: MOV R0,-(SP) ADD #255.,R0 ; ADJUST FOR KEX PARTIAL READ/WRITE CLRB R0 ; CLEAR LOW BYTE SWAB R0 ; R0 = number of blocks ADD R0,R1 ; Are we exceeding the partition ? BCC 2$ ; No, branch ; Overflow occur - need to adjust the wordcount - R1 contains excess. TST R1 ; Is it exactly zero ? BEQ 2$ ; YES, NO ADJUST NECESSARY SUB R1,R0 ; R0 = Adjusted blocks SWAB R0 ; Make R0 into a wordcount. TST (SP)+ ; POP R0 - not needed. BIS #HDERR$,@-(R5) ; Set hard error indicator in CSW BR 3$ ; SKIP NEXT 2$: MOV (SP)+,R0 ; Restore old wordcount 3$: ASL R0 ; Convert to a byte count. MOV R0,P.BCNT(R4) ; Store in the MSCP buffer. .IF EQ MMG$T MOV .UTTAB,R5 ; R5 -> UTTAB entry. .IFF MOV X.UTTAB,R5 ; R5 -> UTTAB ENTRY .ENDC ;EQ MMG$T MOVB UT.PART(R5),P.PART(R4) ; Get the hi order logical block no. RTS PC ; Return to the caller. .IF NE MMG$T .SBTTL BUSCHK - Check DMA limits for a given bus ;+ ; ;- .ENABL LSB BUSCHK: MOV R5,-(SP) ; SAVE R5 MOV @#SYSPTR,R3 ; R5 -> RMON BASE BIT #QBUS$,CONFG2(R3) ; Q-BUS ? BNE 20$ ; YES, BRANCH; NO CHECK NEEDED! MOV H$CQE,R5 ; R5 -> CURRENT Q-ELEMENT ADD #Q$BUFF,R5 ; R5 -> Q$BUFF CALL @H$MPPT ; Convert virtual to physical MOV (SP)+,R1 ; R1 = LOW 16-BITS MOV (SP)+,R2 ; R2 = HIGH BITS ASH #-4,R2 ; SHIFT BITS INTO RIGHT PLACE MOV (R5)+,-(SP) ; STORE WORDCOUNT BPL 10$ ; SKIP IF PLUS NEG (SP) ; MAKE SP = | SP | 10$: ASL (SP) ; MAKE INTO A BYTE COUNT ADD (SP)+,R1 ; ADD BYTES TO READ ADC R2 ; TO UPPER BITS TOO! BIC #<^B11>,R2 ; CLEAR ALL BUT ALLOWED 2 BITS BNE 30$ ; BRANCH IF MORE THAT 18-BITS! 20$: TST (PC)+ ; SKIP NEXT CLEAR CARRY 30$: SEC ; SET CARRY MOV (SP)+,R5 ; RESTORE REGISTER RETURN ; RETURN .DSABL LSB .ENDC ;NE MMG$T .IF NE ERL$G .SBTTL ERROR LOGGING SUPPORT ROUTINES ;+ ; Termination routine to log successful I/O transfers. ;- LOGIT: .IF EQ MMG$T TST $SUCS ; Is success logging enabled? .IFF TST X$SUCS ; IS SUCCESS LOGGING ENABLED .ENDC ;EQ MMG$T BNE 44$ ; NO, BRANCH CLR RETRY ; Retry count = success + 1 MOV #400,RETRY ; Set low bit in high byte ; for correct device id later JSR PC,ERRLOG ; Tell the logger the good news. 44$: JMP DUEXIT ; Take the normal exit. ;+ ; Subroutine to log I/O errors (fatal). ;- BADIO: MOV #1,RETRY ; Set retry count = hard error + 1 JSR PC,ERRLOG ; Log the error. JMP DUHERR ; Take the error exit. ;+ ; Subroutine to log online errors. ;- ERRLOG: .IF EQ MMG$T .ADDR #MBUFF,R2 ; R2 -> response buffer. .IFF MOV @#KISAR1,R1 ; R1 -> INPUT BUFFER PAR1 VALUE MOV #MBUFF,R2 ; R2 -> INPUT BUFF PAR1 VIRTUAL $REL .-2 MBUFF DUX MOV #ERLBUF,R4 ; R4 -> OUTPUT BUFF VIRTUAL ADDR $REL .-2 ERLBUF DUR ;*NOTE* The BLKMOV routine uses user mapping for the output buffer, ; therfore we are not guaranteed that the mapping would be correct. MOV R4,R3 ; SAVE A COPY TEMPORARY BIC #^C<77>,R4 ; KEEP LOW SIX BITS... ADD #BEGREL,R4 ; FORM A PAR1 BIAS VIRTUAL ADDR ASH #-6,R3 ; ADDR/32 FOR PAR1 VALUE BIC #^C<1777>,R3 ; CLEAR SIGN EXTEND MOV #P.MSIZ/2,R5 ; WORDCOUNT MOV H$P1EX,R0 ; R0 -> P1EXT CALL BLKMOV(R0) ; GO MOVE DATA TO LOW MEMORY MOV #ERLBUF,R2 ; R2 -> RESPONSE BUFFER $REL .-2 ERLBUF DUR .ENDC ;EQ MMG$T ADD #P.UNIT,R2 ; Start at the unit number. .IF EQ MMG$T MOV DUCQE,R5 ; R5 -> current queue element. .IFF MOV H$CQE,R5 ; R5 -> CURRENT QUEUE ELEMENT .ENDC ;EQ MMG$T MOV RETRY,R4 ; R4 = Current retry count ADD #DU$COD*400-1,R4 ; Device code / Remaining retries MOV #NRETRY*400+20.,R3 ; R3 <- max retries + 20 words. .IF EQ MMG$T CALL @$ELPTR ; Call the error logger. .IFF CALL @H$ELPT ; CALL ERROR LOGGER (HIGH MEMORY) .ENDC ;EQ MMG$T RTS PC ; Return to the caller. .ENDC ;NE ERL$G .SBTTL ROUTINE TO RETURN THE VOLUME SIZE (SPFUN 373) ;+ ; If the volume is greater than 65K, then the volume size is the ; partition size. All partitions except the last one are = 65K. ;- GETSIZ: MOV P.UNSZ(R5),VOLSIZE ; Get the low part MOV P.UNSZ+2(R5),VOLSIZE+2 ; Get the high part .BR SNDSIZ ; MERGE SNDSIZ: .IF EQ MMG$T MOV DUCQE,R4 ; R4 -> current queue element. MOV .UTTAB,R5 ; R5 -> translation table entry. .IFF MOV H$CQE,R4 ; R4 -> CURRENT QUEUE ELEMENT MOV X.UTTAB,R5 ; R5 -> translation table entry. .ENDC ;EQ MMG$T MOV VOLSIZE,-(SP) ; Assume low order size it total. BNE 5$ ; Is the size exactly 65K? DEC (SP) ; Yes, then subtract 1. 5$: TST VOLSIZE+2 ; Is it greater than 65K? BEQ 10$ ; No, then the low order is it. CMPB UT.PART(R5),VOLSIZ+2 ; Are we the last partition. BGE 10$ ; Yes, then low order is part size. MOV #177777,(SP) ; No, then partition is 65K. 10$: .IF NE MMG$T JSR PC,@H$PTWR ; Return the size to the caller. .IFF MOV (SP)+,@Q$BUFF(R4) ; Return the size to the caller. .ENDC ;NE MMG$T JMP DUEXIT ; Take the normal RT-11 exit. .SBTTL SUBROUTINE TO INITIALIZE MSCP COMMAND BUFFER ;+ ; This subroutine clears the MSCP command buffer and inserts the generic ; parameters required my most command. ; ; Output: R0,R1 destroyed! ;- R4 -> Command buffer GETCBF: MOV R0,-(SP) ; Save work register MOV R1,-(SP) ; ditto .IF EQ MMG$T .ADDR #MBUFF,R4 ; R4 -> message buffer. MOV R4,MRING ; Tell MSCP server where it is. CLR MRING+2 .IFF MOV XPHYSL,R0 ; R0 = GLOBAL REGION PHY BASE LOW MOV XPHYSH,R1 ; R1 = GLOBAL REGION PHY BASE HIGH ADD #MBUFF-DUXBASE,R0 ; ADD OFFSET TO RESPONSE BUFF ADC R1 ; ADD CARRY TO HIGH PART MOV R0,XMRING ; STORE LOW PART MOV R1,XMRING+2 ; STORE HIGH PART MOV #MBUFF,R4 ; R4 = OFFSET TO MESSAGE RING (PAR1) $REL .-2 MBUFF DUX .IFTF MOV #P.CSIZ/2,-(SP) ; Count words to clear on stack which .ASSUME MBUFF EQ CBUFF+P.CSIZ+4 1$: CLR -(R4) ; Clear out a word of the buffer. DEC (SP) ; Whole buffer cleared (ie.=-1)? BPL 1$ ; No, keep clearing. SUB (SP)+,-(R4) ; Add 1 to the sequence number. ; (subtracting -1) MOV #60,-4(R4) ;STORE SIZE OF COMMAND! MOV #60,P.CSIZ(R4) ;STORE RESPONSE SIZE .IFT MOV R4,CRING ; Save the ring pointer. CLR CRING+2 .IFF MOV XPHYSL,R0 ; R0 = GLOBAL REGION PHY BASE LOW MOV XPHYSH,R1 ; R1 = GLOBAL REGION PHY BASE HIGH ADD #CBUFF-DUXBASE,R0 ; ADD OFFSET TO COMMAND BUFFER ADC R1 ; ADD CARRY TO HIGH PART MOV R0,XCRING ; STORE LOW PART MOV R1,XCRING+2 ; STORE HIGH PART .IFTF MOV (R5)+,P.OPCD(R4) ; Select the opcode. .IFT MOV @.UTTAB,P.UNIT(R4) ; Select the unit number. .ASSUME UT.UNIT EQ 0 .IFF MOV #X.UTTAB+2,R1 ; R1 -> UTTAB+2 $REL .-2 X.UTTAB+2 DUX MOV @-(R1),P.UNIT(R4) ; SELECT UNIT NUMBER .IFTF MOV (SP)+,R1 MOV (SP)+,R0 RTS R5 ; Return to the caller. .ENDC ;EQ MMG$T .SBTTL SUBROUTINE TO BYPASS RT-11 AND ISSUE MSCP DIRECTLY ;+ ; ; This subroutine implements special function BYPASS (code 371 and 360) ; which allows the user to issue MSCP commands directly to the disk ; system. ; ; *** NOTE *** ; RT has already address checked the start of the user's ; buffer to ensure that it is in the user's job space. ; ; If the WCNT argument is non-zero, it specifies the virtual ; address of the data buffer. It is converted to physical ; and placed in the command message. If it is zero, the ; physical address specified in the command message is used. ; ;- .ENABL LSB BYPASS: ADD #Q$BUFF-Q$BLKN,R5 ;Advance to buffer address field .IF EQ MMG$T MOV (R5)+,R4 ;R4 = User's buffer address CLR R0 ;Fake high order of zero .IFF CALL @H$MPPT ; Convert virtual to physical. MOV (SP)+,R4 ;R4 = Low order buffer address MOV (SP)+,R0 ;R0 = (shifted) High order address ASH #-4,R0 ;Right-justify high-order address bits .ENDC ;EQ MMG$T ADD #4,R4 ;Adjust for length and virtual circuit ; words ADC R0 ;In case of 16-bit wrap .IF EQ MMG$T MOV R4,MRING ;Set response buffer low-order MOV R0,MRING+2 ; and high-order address .IFF MOV R4,XMRING ; SET RESPONSE BUFFER LOW-ORDER MOV R0,XMRING+2 ; and high-order address .ENDC ;EQ MMG$T .IF EQ MMG$T BNE 30$ ;High-order component only valid in XM .ENDC ;EQ MMG$T ADD #P.MSIZ+4,R4 ;Adjust for response buffer, ; length and virtual circuit words ADC R0 ;In case of 16-bit wrap .IF EQ MMG$T MOV R4,CRING ;Set command message buffer low-order MOV R0,CRING+2 ; and high-order address .IFF MOV R4,XCRING ; SET CMD MESSAGE BUFFER LOW-ORDER MOV R0,XCRING+2 ; AND HIGH ORDER ADDR .ENDC ;EQ MMG$T .IF EQ MMG$T BNE 30$ ;High-order component only valid in XM .ENDC ;EQ MMG$T MOV (R5),R0 ;Data buffer address specified? BEQ 20$ ;Nope, use the command message as is .IF EQ MMG$T CLR R1 ;R1 = High order buffer address MOV R0,R2 ;R2 = Low order buffer address .IFF MOV H$P1EX,R1 ; R1 -> P1EXT CALL CVAPHY(R1) ;Convert user virtual address to phy .ENDC ;EQ MMG$T MOV Q$BUFF-Q$WCNT(R5),R4 ;R4 -> User's command/response buffer ADD #<4+P.MSIZ+4>+P.BUFF,R4 ;Adjust to data buffer address field .IF NE MMG$T MOV Q$PAR-Q$WCNT(R5),10$ ;Save PAR1 value for mapping buffer JSR R0,@H$P1EX ;Externalize the following code .WORD 10$-. .ENDC ;NE MMG$T MOV R2,(R4)+ ;Set the data buffer low-order and MOV R1,(R4)+ ; high-order physical address .IF NE MMG$T 10$: .BLKW ; : PAR1 value to map user's buffer .ENDC ;NE MMG$T 20$: RTS PC ;Return (COORD will start operation) .IF EQ MMG$T 30$: TST (SP)+ ; Tidy the stack JMP DUHERR ; And take the error exit .ENDC ;EQ MMG$T .DSABL LSB .SBTTL ROUTINE TO RETURN THE UNIT TRANSLATION TABLE ;+ ; This routine implements special function 372. It returns the unit ; translation table to the caller if q$wcnt is plus, and writes the ; provide table if the q$wcnt is minus. ;- .ENABL LSB DOTAB: MOV R0,-(SP) ; We need R0 as a worker. MOV #UT.ESZ/2*8.+2,R0 ; R0 = number of words to transfer. MOV R5,R4 ; R4 -> queue element for putword. .IF EQ MMG$T .ADDR #DUTAB,R5 ; R5 -> unit translation table. .IFF MOV #XDUTAB,R5 ; R5 -> UNIT TRANSLATION TABLE $REL .-2 XDUTAB DUX .ENDC ;EQ MMG$T TST Q$WCNT(R4) ; READ TABLE ? BMI 20$ ; NO, BRANCH 10$: .IF EQ MMG$T MOV (R5)+,@Q$BUFF(R4) ; Send a word to the user. ADD #2,Q$BUFF(R4) ; Move to the next buffer location. .IFF MOV (R5)+,-(SP) ; Put the word on the stack. CALL @H$PTWRD ; Send it to the user. .ENDC ;EQ MMG$T DEC R0 ; All done? BGT 10$ ; No, continue in transfer loop. BR 40$ 20$: ASL R0 ; MAKE WORDCOUNT INTO BYTE COUNT .IF EQ MMG$T 30$: MOVB @Q$BUFF(R4),(R5)+ ; STORE BYTE INC Q$BUFF(R4) ; POINT TO NEXT BYTE .IFF 30$: JSR PC,@H$GTBY ; GET BYTE FROM USER MOVB (SP)+,(R5)+ ; STORE THE BYTE IN TABLE .ENDC ;EQ MMG$T DEC R0 ; All done? BGT 30$ ; No, continue in transfer loop 40$: MOV (SP)+,R0 ; Restore R0. JMP DUEXIT ; And take the all done exit. .DSABL LSB .IF NE DU$BBR .SBTTL GETUS - 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 ;**************************************************************************** GETUS: MOV #MBUFF,R5 ; R5 -> END MESSAGE (ONLINE) $REL .-2 MBUFF DUX MOV ACTBBR,R1 ; R1 -> ACTIVE BBR MOV P.UNFL(R5),(R1)+ ; DOES IT SUPPORT BBR ? BPL 10$ ; NO, WE MUST DO IT! JMP DISPAT ; SKIP THE REST! 10$: MOV P.UNSZ(R5),(R1)+ ; STORE MAX LOW LBN .ASSUME BB.MAX EQ 2 MOV P.UNSZ+2(R5),(R1)+ ; STORE MAX HIGH LBN DO IGETUS,THEN,SAVUS ; GET UNIT STATUS, SAVE IT HERE SAVUS: MOV #MBUFF,R5 ; R5 -> END MESSAGE (ONLINE) $REL .-2 MBUFF DUX MOV ACTBBR,R1 ; R1 -> BBR CONTROL TABLE ADD #BB.RCT,R1 ; R1 -> RCT SIZE ENTRY MOV P.RCTS(R5),(R1)+ ; STORE RCT SIZE .ASSUME BB.RCT EQ 6 MOV P.TRCK(R5),(R1)+ ; STORE LBNs/TRACK .ASSUME BB.TRK EQ 10 MOV P.RBNS(R5),@R1 ; STORE RBNS - RCT COPIES .ASSUME BB.RBN EQ 12 .ASSUME BB.COP EQ 13 BIT #UF.WPH,P.UNFL(R5) ; IS IT WRITE PROTECTED ? BEQ ONLBBR ; NO, GO AHEAD... JMP DISPAT ; CONTINUE HAPPILY. .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL BBR - Host Bad Block Replacement Main Entry points ;+ ; 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 ;***************************************************************************** .ENABL LSB BBFND: MOV ACTBBR,R2 ; R2 -> BBR CONTROL BLOCK TST @R2 ; CONTROLLER SUPPORTS BBR ? BMI 20$ ; YES, BRANCH; NO HOST BBR CALL CLRWRK ; GO CLEAR WORK AREA! MOV MBUFF+P.FBBK,BLBNL ; STORE BAD BLOCK LOW MOV MBUFF+P.FBBK+2,BLBNH ; STORE BAD BLOCK HIGH .HP1EX P1LOW ; START OF P1EXT BLOCK MOV @#STATU$,R1 ; SAVE OLD STATUS $REL .-2 STATU$ DUR .HP1EN ; END OF P1EXT BLOCK MOV R1,OLDSTA ; STORE THE OLD STATUS DO IGETUS,THEN,CKWRTP ; GET UNIT STATUS CKWRTP: MOV ACTBBR,R2 ; R2 -> BBR CONTROL BLOCK MOV MBUFF+P.UNFL,R5 ; R5 = UNIT FLAGS MOV OLDSTA,R4 ; R4 = OLD STATUS BIT #UF.WPH,R5 ; WRITE PROTECTED? BEQ CONBBR ; NO, BRANCH MOV R4,R1 ; RESTORE OLD STATUS 20$: JMP NOBBR ; GO CONTINUE UNUSUALLY... ;+ ; Online entry! ;- ; On Entry: We are mapped to DUADDR. ONLBBR: MOV ACTBBR,R2 ; R2 -> ACTIVE BBR TABLE CALL CLRWRK ; GO CLEAR WORK AREA BIS #US.OLP,USTAT ; INDICATE ONLINE .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 ; IF THERE WAS ANY CONTEXT - WE WILL GO THERE... 10$: BR DOBBR ; GO DO BAD BLOCK REPLACEMENT .DSABL LSB ;+ ; Clear work area routine ;- CLRWRK: MOV #XFRSZ,R3 ; R3 = OOPS OFFSET $REL .-2 XFRSZ DUX MOV #WRKSIZ,R4 ; R4 = SIZE OF WORK AREA ASR R4 ; R4 = WORDCOUNT 10$: CLR (R3)+ ; CLEAR LOCATION DEC R4 ; ARE WE FINISH? BNE 10$ ; NO, KEEP CLEARING RTS PC .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL DOBBR - Main Host BBR algorithm ;***ACTION*** UPDATE THIS! ; The overall flow of bad block replacement is as follows: ; ; 1. RCT is notified by DUDSK that a specified logical block is bad. Alter- ; natively, RCT is notified that a crash occurred in the middle of bad ; block replacement (this is detected while bringing the unit online). ; ; 2. RCT locks out all access to the disk. ; ; 3. Go to step 7 if we are recovering from a crash during phase 1 of ; bad block replacement (phase 1 is determining if the block is bad) ; go to step 11 if recovering from phase 2 (phase 2 is replacing the ; block). ; Otherwise continue with step 4. ; ; 4. The process zeros a sector sized buffer, then reads the current content ; of the bad block into the buffer. The buffer is cleared first for ; the rare case when no data can be transferred. The read is performed ; with error recovery and error correction enabled. ; ; 5. Using a write-compare operation, record the data from the bad block in ; sector 1 of each Replacement Control Table copy. If the data cannot be ; successfully recorded in the table, go to step 18. ; ; 6. Record the bad block's LBN, whether or not the saved data is valid, and ; the fact that we are in phase 1 in sector 0 of each Replacement Control ; Table copy. All other information stored in sector 0 is preserved. If ; sector 0 cannot be read, go to step 18. If sector 0 cannot be written, ; go to step 17. ; ; 7. Write and read test patterns on the suspected bad block to determine ; whether or not it is in fact a bad block. Go to step 9 if it fails. ; ; 8. The process writes the saved data back out to the bad block using a ; write-compare operation. The write-compare is performed with the "force ; error" modifier if and only if the saved data is invalid. Go to step 13 ; if the write-compare both succeeds AND the block is no longer reported as ; a bad block -- the original problem was a transient. The write-compare ; succeeds if no error is detected and the saved data is valid or if only a ; forced error is detected and the saved data is invalid. ; ; 9. RCT scans the Replacement Control Table and determines what new RBN the ; bad block should be replaced with, whether or not the the bad block ; has been previously replaced, and (if it has previously been replaced) ; the bad block's old RBN. If the scan fails, go to step 17. ; ; 10. Record the fact that we're in phase 2, the new RBN, whether or not the ; block has been previously replaced, and (if has been replaced before) the ; old RBN in sector 0 of each copy of the Replacement Control Table. Sector ; 0 must not be read again, and go to step 16 if it can not be written. ; ; 11. RCT updates the Replacement Control Table to show the bad block has been ; replaced with the new RBN, and that the old RBN (if any) is unusable. If ; this requires updating two blocks in the RCT, then both blocks must be ; read before either is written. If a block cannot be read successfully, ; go to step 16. If a block cannot be written, go to step 15. ; ; 12. The process uses the REPLACE command to revector the bad block to the ; chosen replacement block, then uses the standard WRITE command (addressed ; to the bad block's LBN) with the "compare" modifier asserted to store the ; saved data in the replacement block. The write-compare is performed with ; the "force error" modifier if and only if the saved data is invalid. ; If the REPLACE command fails, go to step 15. If the WRITE command fails, ; go to step 9 to re-scan the Replacement Control Table for another RBN. ; Note: the current new RBN will become the old RBN for this next pass. ; The WRITE command succeeds if no error is detected and the saved data ; is valid or if only a forced error is detected and the saved data is ; invalid. ; ; 13. RCT updates sector 0 of the Replacement Control Table to indicate that it ; is no longer in the middle of replacing a bad block. The table must be ; updated without reading a new copy of the table from the disk. If the ; table cannot be updated, go to step 17. ; ; 14. RCT releases the lock it acquired in step 2 and exits. ; ; 15. RCT restores the Replacement Control Table to indicate that the new RBN ; is unallocated and usable and that the bad block is either not replaced ; or revectored to the old RBN, whichever was it's original status. The ; table must be updated without reading any blocks from it, instead using ; the copies of the relavent blocks that were read in step 11. Any errors ; are 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 "force ; error" modifier if and only if the saved data is invalid. Any errors are ; ignored. ; ; 17. RCT updates sector 0 of the Replacement Control Table copies to indicate ; that it is no longer in the middle of replacing a bad block. The table ; must be updated without reading sector 0. Any errors are ignored. ; ; 18. RCT releases the lock it acquired in step 2. ; ; Whenever a disk is brought online to a host (MSCP definition of online), RCT ; must check sector 0 of the Replacement Control Table copies to see if a ; failure occurred part way through bad block replacement. If so, the saved ; data is obtained from sector 0 of the table, and the bad block replacement ; algorithm is then resumed. ;- ;**************************************************************************** ; STEP 3 ;**************************************************************************** ;+ ; READ BLOCK RCT0 SO WE HAVE FLAGS, BAD LBN, ETC., IN MEMORY. ;- DOBBR: MOV #SEC0,DSKBF ; SAVE BUFF VIRTUAL ADDR $REL .-4 SEC0 DUX MOV BB.MAX(R2),LBNL ; SET DISK ADDRESS, LOW ORDER MOV BB.MAX+2(R2),LBNH ; SET DISK ADDRESS, HIGH ORDER CALL MULRD ; READ SECTOR 0 OF TABLE BCC 20$ ; IF CC OK JMP VERXIT ; FAILED TO READ SECTOR 0 20$: BIT #US.OLP,USTAT ; UNIT BEING BROUGHT ONLINE? BEQ READIT ; NO, SKIP THE REST! ; ; 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. ; .ENABL LSB MOV #SEC0,R0 ; R0 -> SECTOR 0 $REL .-2 SEC0 DUX ADD #RCTFLG,R0 ; GET ADDRESS OF SECTOR 0 FLAG BIT #,(R0) ; ANYTHING INTERRUPTED? BEQ 40$ ; NO, BRANCH MOV #LBN,DSKBF ; SET BUFFER ADDRESS $REL .-4 LBN DUX MOV BB.MAX(R2),LBNL ; SET DISK ADDRESS, LOW ORDER MOV BB.MAX+2(R2),LBNH ; SET DISK ADDRESS, HIGH ORDER ADD #1,LBNL ; POINT IT TO RCT SEC1; WHERE ADC LBNH ; .. DATA FROM BAD LBN IS SAVE CALL MULRD ; READ SECTOR 1 OF TABLE BCC 10$ ; IF CC OK JMP VERXIT ; FAILED TO READ SECTOR 0 10$: BIT #RF.P1,(R0) ; RECOVER FROM PHASE 1 ? BEQ 30$ ; IF EQ NO CALL P1REC ; RECOVER PHASE 1 DATA ???? BR RTEST ; 30$: BIT #RF.P2,(R0) ; RECOVER FROM PHASE 2 ? BEQ 40$ ; IF EQ NO CALL P2REC ; RECOVER PHASE 2 DATA ???? JMP RCTUPD ; 40$: JMP COMXIT ; PERFORM EXIT PROCESSING .DSABL LSB ;**************************************************************************** ; STEP 4 ;**************************************************************************** ; ; READ THE DATA FROM THE BAD LBN ; READIT: .ENABL LSB MOV #4,R4 ; TRY FOUR TIMES ... 30$: MOV #LBN,R3 ; GET BUFFER ADDRESS $REL .-2 LBN DUX MOV R3,DSKBF ; SET BUFFER ADDRESS MOV #256.,R0 ; GET NUMBER OF WORDS TO ZERO 10$: CLR (R3)+ ; CLEAR OUT THE BUFFER DEC R0 ; DONE? BNE 10$ ; IF NE NO MOV BLBNL,LBNL ; LBNL = LBN, LOW ORDER MOV BLBNH,LBNH ; $LBNH = LBN, HIGH ORDER MOV #OP.RD,IOFUN ; SET FUNCTION CODE CLR IOMOD ; NO MODIFIER CALL DSKIO ; READ THE BAD LBN BCC 20$ ; IF CC SUCCESSFUL BITB #ST.DAT,IOST ; INVALID DATA ? BEQ 20$ ; NO, BRANCH DEC R4 ; YES, LETS RETRY... BNE 30$ ; BEFORE FLAGGIN FE ERROR! BIS #RF.FE,SEC0+RCTFLG ; INDICATE INV DATA FORCED ERR ;**************************************************************************** ; STEP 5 ;**************************************************************************** ; ; WRITE THE DATA TO SECTOR 1 OF EACH REPLACEMENT CONTROL TABLE COPY ; 20$: MOV BB.MAX(R2),LBNL ; TABLE SECTOR 0 LBN, LOW ORDE MOV BB.MAX+2(R2),LBNH ; TABLE SECTOR 0 LBN, HIGH ORD ADD #1,LBNL ; TABLE SECTOR 1 LBN, LOW ORDE ADC LBNH ; ... AND HIGH ORDER CALL MULWT ; MULTI-COPY WRITE SECTOR 1 BIS #RF.P1,SEC0+RCTFLG ; PHASE 1 FLAG BIT SEC 0 BCC PHASE1 ; IF CC OK JMP VERXIT ; FAILURE, CAN NOT WRITE ANY .DSABL LSB ;**************************************************************************** ; STEP 6 ;**************************************************************************** ; ; R0 -> SECTOR 0 - CRTFLG FLAG (PAR1 ADDRESS) ; PHASE1: MOV #SEC0,R0 ; R0 -> SEC0 $REL .-2 SEC0 DUX ADD #RCTFLG,R0 ; GET ADDRESS OF SECTOR 0 FLAG CMP (R0)+,(R0)+ ; SKIP CRTFLG AND RESERVED WRD MOV BLBNL,(R0)+ ; RECORD BAD LBN IN SECT 0 BF MOV BLBNH,(R0)+ ; ... AND THE HIGH BITS MOV #SEC0,DSKBF ; SET BUFFER ADDRESS $REL .-4 SEC0 DUX MOV BB.MAX(R2),LBNL ; SET LBN OF RCT SEC0 MOV BB.MAX+2(R2),LBNH ; ... ; -- Here, Phase 1 flag is written to RCT CALL MULWT ; WRITE UPDAT SECT 0 TO DISK BCC ALLOUT ; GO FOR THE ALLOUT TEST JMP VERXIT ; GO TO FAILURE EXIT ;**************************************************************************** ; STEP 7 ;**************************************************************************** ; REPEAT THIS WHOLE PROCEDURE 8 TIMES OR TILL AN ERROR OCCURS .ENABL LSB ALLOUT: MOV #8.,R5 ; LOAD COUNT ; READ FOUR TIMES RTEST: MOV #4,R4 ; INTERMIDIATE COUNT 10$: MOV #RCTBF,DSKBF ; STORE BUFFER ADDRESS $REL .-4 RCTBF DUX 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 CALL DSKIO ; GO DO IT BCS SCAN ; BRANCH IF ERROR! DEC R4 ; ARE WE FINISH ? BNE 10$ ; NO, REPEAT... ; WRITE - THEN READ FOUR TIMES RWTEST: MOV #OP.WR,IOFUN ; STORE FUNCTION MOV #LBN,DSKBF ; STORE BUFFER ADDR TO WRITE $REL .-4 LBN DUX MOV #MD.CMP,IOMOD ; COMPARE MODIFIER CALL DSKIO ; GO WRITE IT! BCS SCAN ; BRANCH IF ERROR MOV #4,R4 ; LOAD READ COUNT 20$: MOV #OP.RD,IOFUN ; LOAD OPCODE MOV #RCTBF,DSKBF ; LOAD BUFFER TO READ TO $REL .-4 RCTBF DUX CALL DSKIO ; GO DO IT! BCS SCAN ; BRANCH IF ERROR DEC R4 ; ARE WE FINISH? BNE 20$ ; NO, REPEAT... ; 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 DUX MOV #LBN,R3 ; R3 = OFSET BAD BLOCK DATA $REL .-2 LBN DUX 30$: MOV (R3)+,@R0 ; STORE WORD COM (R0)+ ; 1'S COMPLEMENT DEC R4 ; DONE? BNE 30$ ; IF NE NO MOV #OP.WR,IOFUN ; SET WRITE CODE MOV #,IOMOD ; MODIFY (FE,CMP) CALL DSKIO ; WRITE THE TEST PATTERN BCS SCAN ; BRANCH IF DATA ERROR ; NOW LETS READ (NOTE: SAME MODIFIERS, SAME BUFFER) FOUR TIMES! MOV #4,R4 ; REPEATE COUNT 40$: MOV #OP.RD,IOFUN ; READ CODE CALL DSKIO ; READ THE TEST PATTERN BCS SCAN ; BRANCH IF DATA ERROR DEC R4 ; SHOULD WE TRY AGAIN ? BNE 40$ ; YES, BRANCH DEC R5 ; ARE WE COMPLETLY FINISH? BNE RTEST ; NO - THERE WE GO AGAIN! .DSABL LSB ;**************************************************************************** ; STEP 13 ;**************************************************************************** ;+ ; WRITE-COMPARE SAVED DATA BACK TO "BAD" LBN - PROBLEM MAY HAVE BEEN TRANSIENT ;- RESDAT: CALL WTSAV ; WRITE SAVED DATA TO BAD LBN BCS SCAN ; IF CS, LBN STILL BAD JMP NOREP ; No, transient error. ;**************************************************************************** ; STEP 8 - 9 ;**************************************************************************** ; ; SCAN REPLACEMENT CONTROL TABLE AND DETERMINE THE RBN ; SCAN: CALL WTSAV ; WRITE SAVED DATA TO BAD LBN CALL HASH ; FND WHERE TO SEARCH SCAN1: INC BBRECU ; UPDATE PASS THRU HERE. CMP #2,BBRECU ; HAVE WE FOUND A 2ND BAD RBN? BEQ 10$ ; YES, SKIP - FAILURE CALL SRCH ; SEARCH THE TABLE FOR THE RBN BCC PHASE2 ; CARRY CLEAR, GO TO PHASE 2 10$: JMP SAVXIT ; IF CS, FAILURE ;**************************************************************************** ; STEP 10 ;**************************************************************************** ; ; UPDATE SECTOR 0 ON THE DISK WITH THE FACT THAT WE ARE IN PHASE 2 ; PHASE2: BIC #RF.P1,SEC0+RCTFLG ; SHOW WE'VE GONE FROM PHASE 1 BIS #RF.P2,SEC0+RCTFLG ; ... TO PHASE 2 MOV #SEC0,R0 ; R0 -> SEC 0 $REL .-2 SEC0 DUX ADD #URBNL,R0 ; POINT TO NEW RBN SAVE AREA.. MOV NRBNL,(R0)+ ; RECORD NEW RBN IN SECTOR 0 MOV NRBNH,(R0)+ ; ... MOV ORBNL,(R0)+ ; RECORD OLD RBN (IF ANY) MOV ORBNH,(R0) ; ... MOV #SEC0,DSKBF ; PUT BUFFER ADDRESS IN DPB $REL .-4 SEC0 DUX MOV BB.MAX(R2),LBNL ; PUT LBN FOR TABLE SECTOR 0 MOV BB.MAX+2(R2),LBNH ; ... ; -- RCT phase 1 flag cleared, phase 2 flag set CALL MULWT ; UPDATED SECT 0 BACK TO DISK BCC RCTUPD ; GOOD WRITE, DO RCT UPDATE JMP VERXIT ; GO TO FALURE EXIT, COULD NOT WRITE ; ... ANY OF THE SEC0 BLOCKS ;**************************************************************************** ; STEP 11 ;**************************************************************************** ; ; UPDATE REPLACEMENT CONTROL TABLE WITH RBN ; RCTUPD: CALL NEWBF ; SET PARAMET FOR NEW RBN I/O ; R0 = RCTBF MOV R0,DSKBF ; SET BUFFER ADDRESS CALL MULRD ; RD SEC IN TABLE OF NEW RBN BCS 25$ ; 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 RBN FLAG BITS 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 15$ ; NO, IT MUST BE SECONDARY 10$: BIS #DF.SEC,(R3) ; INDICATE NEW RBN IS SECONDAR 15$: BIT #RF.BR,SEC0+RCTFLG ; IS THERE AN OLD RBN? BEQ 40$ ; IF EQ NO ; We are replacing an old RBN. CMP OLBNL,NLBNL ; OLD RBN SECTOR =NEW RBN SEC? BNE 20$ ; IF NE NO CMP OLBNH,NLBNH ; MAYBE, HOW ABOUT HIGH ORDER? BEQ 30$ ; YES, THEY ARE THE SAME ; The old and new RBN are in different RCTs 20$: CALL OLDBF ; SET PARAMETERS FOR OLD RBN MOV R0,DSKBF ; SET BUFFER ADDRESS CALL MULRD ; READ SEC IN TABLE OF OLD RBN 25$: BCS HSAVXIT ; IF CS READ FAILURE, EXIT 30$: 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 MULWT ; WR OLD RBN SECTO BACK TO RCT BCS UNAXIT ; UNABLE TO WRITE ANY RCTs CMP OLBNL,NLBNL ; OLD RBN SEC= NEW RBN SECTOR? BNE 40$ ; IF NE NO CMP OLBNH,NLBNH ; MAYBE, HOW ABOUT HIGH ORDER? BEQ REPLBN ; YES, THEY ARE THE SAME 40$: CALL NEWBF ; SET PARAM FOR NEW RBN I/O MOV R0,DSKBF ; SET BUFFER ADDRESS CALL MULWT ; WRITE A SECTOR BACK TO TABLE BCS UNAXIT ; IF CS WRITE FAILED, EXIT GRACEFULLY ;**************************************************************************** ; STEP 12 ;**************************************************************************** ; ; REPLACE BAD LBN WITH RBN AND WRITE SAVED DATA IN RBN ; REPLBN: 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) ; 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 10$: CALL GENIO ; PERFORM THE REPLACE BCS RPLERR ; IF C Set REPLACE FAILED. ; Read RBN - it should have a forced error from manufacturer MOV #OP.RD,IOFUN ; GET READ MODIFIER MOV #RCTBF,DSKBF ; GET BUFFER $REL .-4 RCTBF DUX MOV #MD.CMP,IOMOD ; GET MODIFIER CALL DSKIO ; GO READ IT BCC RPLERR ; REPLACE FAILED! CMPB #ST.DAT,IOST ; FORCED ERROR ? BNE RPLERR ; NO, REPLACE FAILED! CALL WTSAV ; WRITE SAVED DATA TO RBN BCC FINSZ ; IF CC OK JMP SCAN1 ; FAILED, SCAN ANOTHER NEW RBN ;+ ; EXIT POINTS ;- ;**************************************************************************** ; STEPS - 15 - 16 - 17 - 18 - 19! ;**************************************************************************** HSAVXI: BR SAVXIT ; ... PRESENT NEW RBN WILL BECOME AN OLD RBN RPLERR: CALL FFINSZ ; Update sec 0, RBN alloca MOVB #-2,IOST ; Indicate REPLACE fail erlog BR VERXIT ; REPLACE failed, get out. ; ; UPDATE TABLE SECTOR 0 TO SHOW WE ARE FINISHED REPLACING THE LBN ; NOREP: MOV #-1,OOPS ;it was transient error FINSZ: CALL FFINSZ ; UPDATE TABLE SECTOR 0 BCS VERXIT ; IF CS FAILURE BR COMXIT ; SUCCESSFUL EXIT UNAXIT: ; MARK RBN UNALLOCATED BEFORE CALL UNALL ; SHOW RBN UNALLOCATED SAVXIT: ; RESTORE BAD LBN BEFORE EXIT CALL WTSAV ; WRT SAVE DATA TO BAD LBN CALL FFINSZ ; SHOW WE'RE DONE IN Tbl SEC 0 VERXIT: ; FAILURE EXIT TSTB IOST ; IS I/O STATUS ZERO? BNE 10$ ; NO ; RCT MUST BE FULL MOVB #-1,IOST ; INDICATE FAILURE TO ERR LOG 10$: BIT #US.OLP,USTAT ; ARE WE BRINGING UNIT ONLINE? BNE COMXIT ; YES, DON'T ISSUE ERROR! JMP DUHERR ; GO TO HARD ERROR COMXIT: ; ; ???? The following commented out code is meant for the future ???? ; BIT #US.OLP,USTAT ; ARE WE BRINGING A UNIT ONLIN ; BNE COMXT1 ; YES ;.IIF NE DU$ERL CALL $ERPKT ; LOG REPLACEMENT COMXT1: CLR USTAT ; UPDATE FLAG CLR OOPS ;clear the noreplacement flag JMP START .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL BBRSUB - BBR subroutines .SBTTL DSKIO - 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 = TRASFER SIZE ; ; OUTPUT: ; C = 0 IF SUCCESS ; C = 1 IF FAILURE ;- DSKIO:: ; ISSUE SINGLE BLOCK QIO TO DISK MOV #512.,XFRSZ ; SET TRANSFER SIZE (BYTES) GENIO: PUSH MOV XPHYSL,R4 ; R4 = PHYSICAL LOW ADDRESS MOV XPHYSH,R5 ; R5 = PHYSICAL HIGH ADDR MOV DSKBF,R0 ; R0 = VIRTUAL ADDRESS SUB #BEGREL,R0 ; R0 = OFFSET FROM PHYSICAL ADDRESS ADD R0,R4 ; ADD THE OFFSET... ADC R5 ; INTO THE 28-BIT QUANTITY! MOV R4,DPHYSL ; STORE LOW BUFF MOV R5,DPHYSH ; STORE HIGH BUFF MSCP OP.NIL ; DO NOTHING - LET ROUTINE CHOOSE FUNC ; R4 -> CBUFF (PAR1 = BEGREL) MOV #X.UTTAB+2,R1 ; R1 ->.UTTAB ENTRY (+2) $REL .-2 X.UTTAB+2 DUX MOV @-(R1),P.UNIT(R4) ; STORE THE UNIT NUMBER MOV IOFUN,P.OPCD(R4) ; STORE THE OPCODE MOV IOMOD,P.MOD(R4) ; STORE THE MODIFIERS CMP #OP.RPL,P.OPCD(R4) ; IS THIS A REPLACE ? BEQ 20$ ; YES, BRANCH MOV LBNH,P.LBN+2(R4) ; SAVE HIGH ORDER LBN IN PACKET MOV LBNL,P.LBN(R4) ; SAVE LOW ORDER LBN MOV XFRSZ,P.BCNT(R4) ; SAVE BYTE COUNT MOV DPHYSH,P.BUFF+2(R4) ; STORE BUFFER HIGH MOV DPHYSL,P.BUFF(R4) ; STORE BUFFER LOW BR 30$ ; SKIP NEXT 20$: MOV RPLBH,P.RBN+2(R4) ; PUT HIGH REPLACEMENT BLOCK IN PACKET MOV RPLBL,P.RBN(R4) ; PUT LOW REPLACEMENT BLOCK IN PACKET MOV LBNH,P.LBN+2(R4) ; SAVE HIGH ORDER LBN IN PACKET MOV LBNL,P.LBN(R4) ; SAVE LOW ORDER LBN 30$: CALL SCONTX ; GO SAVE CONTEXT - START POLL ;+ ; ENTRY POINT - From Restore of context. ; ; MAP - PAR1 (DUADDR) ; R1 = BEGREL, R2 = ACTBBR ;- MOV #MBUFF,R4 ; R4 -> MBUFF (PAR1 BIAS) $REL .-2 MBUFF DUX 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 35$ ; No, SKIP NEXT BIC #ST.DAT,2(SP) ; CLEAR THE FORCED ERROR FLAG! 35$: CLC ; ASSUME SUCCESS MOVB (SP)+,IOST+1 ; Save flags in highbyte of IOST;004 MOV (SP),IOST+2 ; Save the Full P.STS in 2nd wrd;004 BIC #^C,(SP) ; Save only major code in lowbyt;004 MOVB (SP)+,IOST ; $IOST = Major code+flags ;004 BEQ 50$ ; and if both=0, then success ;004 ;***ACTION*** SOME ERROR LOGGING HERE PERHAPS 40$: SEC ; INDICATE I/O FAILED 50$: POP RETURN ; .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL FFINSZ - 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 WORD 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,R3 ; R3 -> BEG OF SEC0 $REL .-2 SEC0 DUX ADD #RCTFLG,R3 ; R3 = OFFSET TO RCTFLAG MOV #8.,R4 ; R4 = NUMBER OF WORDS TO CLR 10$: CLR (R3)+ ; CLEAR LOCATION DEC R4 ; ARE WE FINISH? BNE 10$ ; NO, REPEAT MOV #SEC0,DSKBF ; SET SECTOR 0 BUFFER ADDRESS $REL .-4 SEC0 DUX MOV BB.MAX+2(R2),LBNH ; SET DISK ADDRESS, HIGH ORDER MOV BB.MAX(R2),LBNL ; ... AND LOW ORDER CALL MULWT ; WRITE SECTOR 0 OF TABLE RETURN ; .SBTTL HASH - PRIMARY REPLACEMENT CONTROL TABLE HASH ALGORITHM ;+ ; HASH - PRIMARY REPLACEMENT CONTROL TABLE HASH ALGORITHM ; ; THIS ALGORITHM PRODUCES THE 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 ;- 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 ORDER ; R1 = (QUO(LBN/TRKSZ)*RBNPT), LO ORDER 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 MAXIMUM USER ADDRESSABLE LBN, LOW ORDER ADC R1 ; ... AND CARRY ADD BB.MAX+2(R5),R1 ; ADD MAXIMUM USER ADDRESSABLE LBN, HIGH ORDER ADD #2,R2 ; ADD 2 ADC R1 ; ... AND CARRY MOV R2,PLBNL ; PLBNL = BLOCK NUMBER IN TABLE, LOW ORDER MOV R1,PLBNH ; PLBNH = BLOCK NUMBER IN TABLE, HIGH ORDER ASL R0 ; We're not gonna save long words, we'll get ASL R0 ; the byte offset into the blck, and save that MOV R0,PLBNO ; PLBNO = OFFSET WITHIN BLOCK POP ; RESTORE REGISTERS RETURN ; .SBTTL MULRD - PERFORM MULTIPLE-COPY READ FROM DISK ;+ ; MULRD - PERFORM MULTIPLE-COPY READ FROM DISK ; ; THIS ROUTINE IS USED TO READ THE MULTIPLE REPLACEMENT CONTROL TABLE ; COPIES ON THE DISK. EACH COPY IS READ UNTIL ONE SUCCEEDS. THIS ; ROUTINE IS CONSIDERED A SUCCESS IF ANY COPY IS READ SUCCESSFULLY. ; ; INPUT: ; 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 ;- MULRD:: ; PERFORM MULTIPLE-COPY READ FROM DISK PUSH ; SAVE INPUT LBN AS IT GETS CHANGED PUSH ; SAVE REGISTERS 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 MOV #MD.CMP,IOMOD ; SET COMPARE MODIFIER 10$: CALL DSKIO ; PERFORM THE READ BCC 30$ ; IF CC SUCCESS ADD R4,LBNL ; GET ADDRESS OF SECTOR IN NEXT COPY ADC LBNH ; ... DEC R0 ; DONE? BNE 10$ ; IF NE NO, READ NEXT COPY OF TABLE SEC ; OTHERWISE, INDICATE FAILURE 30$: POP ; RESTORE REGISTERS POP ; RESTORE LBN AT ENTRY RETURN ; .SBTTL MULWT - PERFORM MULTIPLE-COPY WRITE TO DISK ;+ ; MULWT - PERFORM MULTIPLE-COPY WRITE TO DISK ; ; THIS ROUTINE IS USED TO WRITE THE MULTIPLE REPLACEMENT CONTROL TABLE ; COPIES ON THE DISK. AN ATTEMPT IS MADE TO WRITE ALL COPIES. THIS ; ROUTINE IS CONSIDERED A SUCCESS IF ANY COPY IS WRITTEN SUCCESSFULLY. ; ; INPUT: ; 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 ;- MULWT:: ; PERFORM MULTIPLE-COPY WRITE TO DISK PUSH ; SAVE LBN AT ENTRY PUSH ; SAVE REGISTERS 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 #MD.CMP,IOMOD ; SET COMPARE MODIFIER CALL DSKIO ; PERFORM THE WRITE BCC 20$ ; IF CC SUCCESS CMPB IOST,#ST.DAT ; Should we rewrite with forced error BNE 25$ ; No, drive or controller problem! exit.;004 MOV #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 REFLACTS THE ; TRUE STATE OF THE RCT TABLES! DEC R4 ; COUNT THE ERROR 20$: ADD R3,LBNL ; GET ADDRESS OF SECTOR IN NEXT COPY ADC LBNH ; ... DEC R0 ; DONE? BNE 10$ ; IF NE NO, WRITE NEXT COPY OF TABLE TST R4 ; DID THE WRITE SUCCEED TO ANY COPY? BNE 30$ ; IF NE YES (TST CLEARED C) 25$: SEC ; OTHERWISE, INDICATE FAILURE 30$: POP ; RESTORE REGISTERS POP ; RESTORE LBN AT ENTRY RETURN ; .SBTTL NEWBF - SET QIO PARAMETERS FOR NEW RBN .SBTTL OLDBF - 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 DUX 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 DUX 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 P1REC - RESTORE DATA FOR PHASE 1 RECOVERY .SBTTL P2REC - RESTORE DATA FOR PHASE 2 RECOVERY ;+ ; P1REC - RESTORE DATA FOR PHASE 1 RECOVERY ; P2REC - RESTORE DATA FOR PHASE 2 RECOVERY ; ; THESE ROUTINES ARE CALLED WHEN RCT IS BRINGING A UNIT ONLINE WHICH WENT ; OFFLINE DURING PHASE 1 OR 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: ; IF ENTERED BY CALL P1REC OR CALL P2REC: ; RCTFLG = INTERNAL FLAG WORD ; BLBNL = BAD BLOCK NUMBER, LO ORDER ; BLBNH = BAD BLOCK NUMBER, HI ORDER ; IN ADDITION, IF ENTERED BY CALL P2REC: ; 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) ;- P1REC:: ; PHASE 1 RECOVERY ; ; RESTORE FLAG WORD AND BAD BLOCK NUMBER ; CMP (R0)+,(R0)+ ; SKIP RCTFLG AND RESERVED WORD MOV (R0)+,BLBNL ; RESTORE BAD BLOCK NUMBER, LO MOV (R0)+,BLBNH ; RESTORE BAD BLOCK NUMBER, HI; AND ;JHC00 CLRB BLBNH+1 ; ZERO THE TOP BYTE (KEEP R0 EVEN) ;JHC00 CALL HASH ; RESTORE PLBNL, PLBNH RETURN ; P2REC:: ; PHASE 2 RECOVERY PUSH ; SAVE REGISTERS CALL P1REC ; RESTORE FLAG WORD AND BAD BLOCK NUMBER ; ; RESTORE NEW RBN ; MOV #NRBNL,R3 ; POINT TO INTERNAL STORAGE AREA $REL .-2 NRBNL DUX 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 ; RESTOR 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 ; ; ; ROUTIE 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 = QUOTIENT, LO ORDER) MOV R1,(R3)+ ; RESTORE SECTOR, HI (R1 = QUOTIENT, HI ORDER) 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 #2,-6(R3) ; ACCOUNT FOR SEC0 & SEC1 OF THE RCT ADC -4(R3) ; ... RETURN ; .SBTTL SRCH - 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 DESRIPTOR ; 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 DECRIPTORS - 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) ;- SRCH:: ; SEARCH REPLACEMENT CONTROL TABLE PUSH ; SAVE REGISTERS CLR ORBNL ; ASSUME NO MATCH CLR ORBNH ; ... CLR R0 ; R0 = RESCAN INDICATOR MOV PLBNO,R4 ; R4 = STARTING OFFSET IN BLOCK (IN BYTES) ASR R4 ; convert it into words, ASR R4 ; ... and into longwords for Ping-Pong search MOV #RCTBF,DSKBF ; USER RCTBF BUFFER ADDRESS $REL .-4 RCTBF DUX MOV PLBNL,LBNL ; GET BLOCK NUMBER IN TABLE MOV PLBNH,LBNH ; ... AND THE HIGH ORDER SNEXT: CALL MULRD ; READ THE BLOCK FROM THE TABLE BCS SXIT ; IF CS WE FAILED TO READ ANY COPY CLR R2 ; R2 = DELTA FROM STARTING OFFSET STEST: MOV R4,R1 ; GET STARTING OFFSET ADD R2,R1 ; OFFSET = STARTING OFFSET + DELTA BLT SBMP ; IF LT OFFSET < 0 CMP R1,#127. ; IS OFFSET > 127 ? BGT SBMP ; IF GT YES ASL R1 ; CHANGE FROM LONGWORD OFFSET TO BYTE OFFSET ASL R1 ; ... TST RCTBF+2(R1) ; UNALLOCATED RBN DESCRIPTOR? $REL .-2 RCTBF+2 DUX BNE SALL0 ; IF NE NO, CONTINUE SEARCHING MOV #NRBNL,R3 ; SHOW WE HAVE A NEW RBN $REL .-2 NRBNL DUX CALL SRBN ; CALCULATE THE NEW RBN CLC ; INDICATE SUCCESS BR SXIT ; AND EXIT SALL0: BIT #DF.ALL,RCTBF+2(R1) ; IS THIS RBN ALLOCATED? $REL .-2 RCTBF+2 DUX BEQ SNULL ; IF EQ NO, CHECK FOR NULL ENTRY MOV RCTBF+2(R1),R3 ; GET WORKING COPY OF DESCRIPTOR HIGH $REL .-2 RCTBF+2 DUX BIC #,R3 ; CLEAR FLAG BITS CMP BLBNH,R3 ; IS IT ALLOCATED TO THE LBN WE ARE REPLACING? BNE SNULL ; IF NE NO, CHECK FOR NULL ENTRY CMP BLBNL,RCTBF(R1) ;MAYBE - HOW ABOUT LOW ORDER WORD? $REL .-2 RCTBF DUX BNE SNULL ; IF NE NO, CHECK FOR NULL ENTRY MOV #ORBNL,R3 ; SHOW WE HAVE AN OLD RBN $REL .-2 ORBNL DUX BIS #RF.BR,SEC0+RCTFLG ; ... CALL SRBN ; CALCULATE THE OLD RBN BR SBMP ; AND CONTINUE SEARCHING SNULL: TST RCTBF+2(R1) ; IS THIS A NULL ENTRY (DF.NUL SET)? $REL .-2 RCTBF+2 DUX BPL SBMP ; IF PL NO TST R0 ; IS THIS A RESCAN? SEC ; ASSUME YES BNE SXIT ; IF NE YES INC R0 ; INDICATE A RESCAN MOV ACTBBR,R4 ; R4 WILL BE CLEAR ANYHOW MOV BB.MAX(R4),LBNL ; GET MAX USER ADDRESSABLE LBN MOV BB.MAX+2(R4),LBNH ; ... ADD #2,LBNL ; START AT TABLE SECTOR 3 ADC LBNH ; ... CLR R4 ; ZERO STARTING OFFSET BR SNEXT ; START RESCAN OF 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 ; ... BR SNEXT ; GO READ THE NEXT SECTOR SRBN: ; CALC RBN FOR PRIMARY, SECONDARY, OR MATCH PUSH ; SAVE REGISTERS ; CALCULATE RBN ($RBNL,$RBNH) ; RBN = ((FINAL LBN - (MXLBN+2))*128)+OFFSET MOV LBNL,R3 ; START WITH RESULTANT BLOCK IN TABLE, LO ORDE 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 SBC R2 ; ... AND THE CARRY MOV #128.,R0 ; 128 = MULTIPLICAND CALL DMUL ; PERFORM DOUBLE PRECISION MULTIPLY ; R0 = (FINAL LBN - (MXLBN+2))*128, HI ORDER ; R1 = (FINAL LBN - (MXLBN+2))*128, LO ORDER MOV 10(SP),R2 ; GET SAVED OFFSET VALUE ASR R2 ; CHANGE FROM 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 ADDRESS ($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 FROM LONGWORD OFFSET TO BYTE OFFSET ASL R2 ; ... MOV R2,(R3) ; STORE OFFSET SXIT: POP ; RESTORE REGISTERS RETURN ; RETURN FROM SRCT OR $SRCH .SBTTL UNALL - 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. ;- UNALL:: PUSH ; SAVE REGISTERS CALL NEWBF ; SET UP DPB FOR NEW RBN ; R0 = RCTBF MOV R0,DSKBF ; SET BUFFER ADDRESS CALL MULRD ; READ RCT BLOCK 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 MULWT ; WRITE OUT BLOCK OF NE RBN 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 CALL MULRD ; READ IN RCT BLOCK FOR OLD 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$ ; YES 10$: BIS #DF.SEC,(R5) ; INDICATE SECONDAY 20$: CALL MULWT ; SET UP DPB FOR NEW RBN ; R0 = $RCTBF 30$: POP ; RESTORE REGISTERS RETURN ; .SBTTL WTSAV - 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). ; ; 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:: ; WRITE SAVED DATA BACK TO LBN MOV BLBNL,LBNL ; PUT LBN IN DPB MOV BLBNH,LBNH ; ... MOV #OP.WR,IOFUN ; WRITE FUNCTION MOV #MD.CMP,IOMOD ; ASSUME SAVED DATA IS VALID BIT #RF.FE,SEC0+RCTFLG ; IS SAVED DATA INVALID? BEQ 10$ ; IF EQ NO MOV #MD.ERR!MD.CMP,IOMOD ; WRITE WITH FORCED-ERROR MODIFIER 10$: MOV #LBN,DSKBF ; SET ADDRESS OF SAVED DATA $REL .-4 LBN DUX CALL DSKIO ; PERFORM THE WRITE-COMPARE RETURN ; .SBTTL DDIV - 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 - 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 THE MULTIPLICAND ROL R2 ; BR 10$ ; AND SEE IF ANY MORE MULTIPLIER 30$: TST (SP)+ ; CLEAN UP THE STACK, RETURN C=0 RETURN ; .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL RCONTX - Restore the context before the interrupt. ;+ ; ;- .ENABL LSB RCONTX: MOV (SP)+,(PC)+ ; STORE RETURN ADDR... RBACK: .WORD 0 ; ...HERE CLR BBRON ; CLEAR FLAG! MOV #CONTEXT,R5 ; R5 -> CONTEXT VALUE $REL .-2 CONTEXT DUX 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 JMP @(SP)+ ; GOTO...WHO KNOWS/CARES... 20$: JMP @RBACK ; RETURN TO CALLER .DSABL LSB .ENDC ;NE DU$BBR .IF NE DU$BBR .SBTTL SCONTX - Save the context - GO START COMMAND! ;+ ; ;- .ENABL LSB SCONTX: MOV #CONTEXT,R0 ; R0 -> FIRST ENTRY TO STORE $REL .-2 CONTEXT DUX 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 #-1,BBRON ; INDICATE BBR IS ON JMP POLL ; GO START COMMAND CONTEXT:.WORD 0 ; SAVE PTR HERE! .REPT 60 ; SIZE OF INTERNAL STACK .WORD 125252 ; LOCATIONS .ENDR .DSABL LSB .ENDC ;NE DU$BBR .IF NE MMG$T ;+ ; DUX CALCULATIONS ;- DUXEND:: DUXSIZ ==: DUXEND - DUXBAS ; SIZE IN BYTES PSECT DUX .IF EQ DU$BBR .ASSUME . LE DUXBAS+<10000> ; < 2048 WORDS .IFF .ASSUME . LE DUXBAS+<20000> ; < 4096 WORDS .ENDC ;EQ DU$BBR .ENDC ;NE MMG$T ;********************************************************************* DUPSECT ;********************************************************************* .SBTTL BOOTSTRAP DRIVER ;+ ; This code implements the standard RT-11 bootstrap. ;- .DRBOT DU,BOOT1,READ,CONTROL= . = DUBOOT+40 BOOT1: JMP @#BOOT-DUBOOT ; Start the boot. .IF NE DU$BOO .SBTTL LOACSR - 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 . = DUBOOT + 14 LOACSR: MOV BUNIT,R3 ; R3 = UNIT NUMBER ASL R3 ; ADJUST (SIZE OF ENTRY = 4) ASL R3 ; R3 = OFFSET INTO UNIT/PORT TABLE BR 100$ ; "DYNAMITE - JUMP!" . = DUBOOT + 44 100$: ADD #PD.POR-DUBOOT,R3 ; R3 -> UNIT/PORT TABLE ENTRY MOV (R3)+,MUNIT ; STORE MSCP UNIT; PTR TO PORT MOV @R3,R3 ; GET PORT INTO R3 ASL R3 ; R3 = OFFSET INTO PORT/CSR TABLE BR 200$ ; "DYNAMITE - JUMP!" . = DUBOOT + 70 200$: ADD #PD.CSR-DUBOOT,R3 ; R3 -> CSR FOR THIS PORT BR 300$ ; "DYNAMITE - JUMP!" . = DUBOOT + 104 300$: MOV @R3,BDUAIP ; STORE THE CSR INTO PROPER LOCATION RTS PC ; RETURN TO CALLER .DSABL LSB .ENDC ;NE DU$BOO . = DUBOOT+120 READ: 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: .IF NE DU$BOO ASR #1 ; Have we figure out BDUAIP ? BCC 10$ ; Yes, skip ! JSR PC,LOACSR ; Go figure out the IP location .ENDC ;NE DU$BOO 10$: MOV #NRETRY,BRETRY ; Set up a retry count. ASL R1 ; Convert word count to byte count. ASR #1 ; Have we initialized the port? BCC READ1 ; Yes, no need to init it. .IF EQ DU$BOO JSR PC,ALTCHK ; Nope, so now check/alter boot CSR ; due to alternate "standard" .ENDC ;EQ DU$BOO ;+ ; 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-DUBOOT,R4 ; Point to list of words to load. 1$: 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 1$ ; No, wait for it. MOV (R4)+,@R5 ; Set the next word. ASL R3 ; Next time, next step. BPL 1$ ; 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. ;+ ; Perform a real read (vice an init) of the disk device. ;- READ1: JSR R0,BGTBUF ; Get and clear the command buffer. .WORD OP.RD ; Set up read command. 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. .ENABL LSB BDOIO: MOV BDUAIP,R3 ; Point to the port CSR. 1$: MOV #BCRING+2-DUBOOT,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. 2$: TST @R3 ; Check if UDA still online. BMI 4$ ; No, need to retry init. TST @R4 ; Did the port take the packet? BMI 2$ ; Nope, spin on a wait. TST -(R4) ; Back up the pointer. MOV #OWN,-(R4) ; Give the port a reply buffer. 3$: TST @R3 ; Is the UDA running?. BMI 4$ ; No, need to retry init. TST @R4 ; Wait for the port to answer back. BMI 3$ ; 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!! RTS PC ; Return, no error. 4$: TST (SP)+ ; Adjust the stack. BR BINIT ; Error so try another init. .DSABL LSB ;+ ; Subroutine to clear the MSCP rings and perform primitive initialization. ;- BGTBUF: MOV #BBUFF-DUBOOT+P.MSIZ,R4 ; Point to buffer. 1$: CLR -(R4) ; Clear out the buffer. CMP R4,#BBUFF-DUBOOT ; Back to start of buffer yet?. BHI 1$ ; No, loop. MOV #P.MSIZ,-4(R4) ; Load command length. MOV (R0)+,P.OPCD(R4) ; Set up the correct opcode. .IF EQ DU$BOO MOV BUNIT,P.UNIT(R4) ; Get the unit number. .IFF MOV MUNIT,P.UNIT(R4) ; Get the unit number. .ENDC ;EQ DU$BOO RTS R0 .SBTTL BOOTSTRAP IMPURE AREA ;+ ; ** 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-DUBOOT ; 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: .BLKB P.MSIZ ; Message/command buff, 1 full packet. .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 ALTCHK - 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. ; ;- BDUAIP: .WORD DU$CSR ; : Address of AIP register .IF NE DU$BOO MUNIT: .WORD 0 ; MSCP unit number; current value .ENDC ;NE DU$BOO .IF EQ DU$BOO ALTCHK: MOV R0,-(SP) ; Save a work register MOV @#INS.V,-(SP) ; Save current vector contents MOV PC,R0 ; Build address to our own routine ADD #BT.INS-.,R0 MOV R0,@#INS.V ; Redirect traps to our routine CLR R0 MFPT ; Get the processor type CMP R0,#4 ; Are we running on a FALCON? BNE 999$ ; Nope... MOV #DU$ALT,BDUAIP ; Yes, so use mongrel "standard" 999$: MOV (SP)+,@#INS.V ; Restore saved vector MOV (SP)+,R0 ; and the work register RTS PC BT.INS: BIS #1,2(SP) ; Set carry RTI ; and return from trap .ENDC ;EQ DU$BOO .IF NE DU$BOO ; Unit/Port table PD.POR: ;>>>note there is probaly not enough space for 64*2 words here, so ; there may be a restriction to the first "n" units as being ; bootable? $1=0 .REPT DU$UNIT .IRP Y,\$1 .WORD DU$U'Y,DU$O'Y .ENDR $1=$1+1 .ENDR PD.CSR: .WORD DU$CSR ; PORT 0 IP .IF NE DU$PORTS-1 .WORD DU$CS1 ; PORT 1 IP .IF NE DU$PORTS-2 .WORD DU$CS2 ; PORT 2 IP .IF NE DU$PORTS-3 .WORD DU$CS3 ; PORT 3 IP .ENDC ; NE DU$PORTS-3 .ENDC ; NE DU$PORTS-2 .ENDC ; NE DU$PORTS-1 .ENDC ;NE DU$BOO .SBTTL BOOTSTRAP PRIMARY ROUTINE ;+ ; A good old primary 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-DUBOOT,@#B$READ ; Store start location for read. MOV #B$DNAM,@#B$DEVN ; Store RAD50 device name. MOV (SP)+,@#B$DEVU ; Store the unit number. JMP @#B$BOOT ; Start secondary boot. .DREND DU .SBTTL OVR1 - Set code overlay number 1 .PSECT SETOVR ; This Psect must be block... OVRBK0: ; ...aligned. ; Define the bias BIAS ==: ; Bias due to overlay ; Define storage locations VALUE: .WORD 0 ; Value to store OFFSET: .WORD 0 ; Offset to primary driver .IF NE DU$BOO ; Offset to second table port/csr in the primary driver OFF2TA = 8.*2*2 .ENDC ;NE DU$BOO .SBTTL SET UNIT, PORT, PARTITION, SUCCESS AND RETRY ;+ ; Set code for MSCP unit, port and partition. ;- SET.UT: DEC R3 ; Convert R3 value to table offset. CMP R0,#255. ; Is this a legal value? BHI HLP1 ; No, take error exit. CMP R3,#UT.PORT ; Is this the SET PORT command? BNE 10$ ; No, then go process the set. CMP R0,#DU$PORTS-1 ; Then is it a legal port number. BHI HLP1 ; No, take error exit. 10$: BIC #100000,R1 ; Strip the "device vs unit" flag. ASL R1 ; Create an index into the RT-11... ASL R1 ; ...to MSCP translation table. .IF NE DU$BOO MOV R1,OFFSET ; ...PRIMARY DRIVER UNIT/PORT TABLE. .ENDC ;NE DU$BOO .ASSUME UT.ESZ EQ 4 .ADDR #UTTAB+BIAS,R1,ADD ; R1 -> UTTAB ADD R3,R1 ; Now it points to the item. MOVB R0,(R1) ; Store it in the table. .IF NE DU$BOO MOV R0,VALUE ; SAVE VALUE TO STORE CMP R3,#UT.PORT ; Was this a SET Port command ? BNE 20$ ; NO, BRANCH ADD #2,OFFSET ; OFFSET INTO PORT ENTRY BR 30$ ; SKIP NEXT 20$: CMP R3,#UT.UNIT ; Was this a SET Unit command ? BNE S.NOR ; NO, BRANCH 30$: MOV #PD.POR/1000,R3 ; SAVE BLOCK NUMBER IN R3 CALL CORWRT ; WRITE BLOCK ZERO - READ N BCS S.IGN ; BRANCH IF ERROR ; R2 -> Buffer ADD OFFSET,R2 ; Get correct offset MOV VALUE,(R2) ; Set the boot-time port CALL CORREA ; WRITE BLOCK N - READ ZERO BCS S.IGN ; BRANCH IF ERROR BR S.NOR ; BRANCH IF NO ERROR .IFF BR S.NOR ; EXIT WITH SUCCESS STATUS .ENDC ;NE DU$BOO .IF NE ERL$G ;+ ; Set code for success error logging. ;- SET.SUC: CLR R3 ; Clear the preset flag. NOP ; No, entry = SET.SUC+4. MOV R3,$SUCS+BIAS ; Set the appropriate value. BR S.NOR ; Exti with success status. .ENDC ;NE ERL$G ;+ ; Set code for retry count ;- SET.RET: CMP R0,R3 ; Is the count too large? HLP1: BHI S.ERR ; Yes, then take error exit. MOV R0,$RETRY+BIAS ; No, then store the value. BR S.NOR ; Exit with success status. .SBTTL SET CSR ;+ ; Set code for CSR(s) ;- SET.CSR: CMP R0,#160000 ; Is this a valid CSR address? BLO S.ERR ; No, then take the error exit. MOV R0,VALUE ; STORE VALUE - TO FREE R0 MOV R3,OFFSET ; Save offs to primary driver table MOV #BLK0,R3 ; Read block zero CALL CORWRT ; Write block 1 - read n BCS S.IGN ; Branch if error! DEC OFFSET ; Convert from 1,3,5,7 to 0,2,4,6 BNE 10$ ; Not 0, don't change install CSR MOV VALUE,INSCSR+BIAS+1000 ; Set the installation CSR 10$: MOV OFFSET,R0 ; Make a copy of the offset NEG R0 ; Negate it for reversed CSR table .ADDR #DISCSR+BIAS+1000,R0,ADD ; Calculate pickly MOV VALUE,(R0) ; Now set the display CSR in question MOV #BLK0,R3 ; Write block n = 0 CALL CORREA ; Write block n - read 1 BCS S.IGN ; Branch if error MOV OFFSET,R3 ; Restore offset into R3 .IF NE DU$BOO ADD #OFF2TA,OFFSET ; OFFSET PORT/CSR TABLE ENTRY TO FILL .ENDC ;NE DU$BOO .IF NE DU$PORTS-1 ASL R3 ; *4 ASL R3 ; *10 .ASSUME PC.ESZ EQ 10 .ADDR #PCTAB+BIAS,R1 ; R1 -> PC table, set CSR there ADD R3,R1 ; Add table entry offset .IFF .ADDR #UDAIP+BIAS,R1 ; R1 -> directly to command. .ENDC ;NE DU$PORTS-1 MOV VALUE,(R1)+ ; Store the AIP address. .ASSUME PC.AIP EQ 0 MOV VALUE,@R1 ; Store the ASA address. .ASSUME PC.ASA EQ 2 ADD #2,@R1 ; It is 2 greater than the AIP. .IF NE DU$PORTS-1 .IF EQ DU$BOO TST R3 ; Is this port 0? .ASSUME PC.AIP EQ 0. BNE S.NOR ; Nope, so leave boot-time CSR alone .ENDC ;EQ DU$BOO .ENDC ;NE DU$PORTS-1 .IF EQ DU$BOO MOV #BDUAIP/1000,R3 ; SAVE BLOCK NUMBER .IFF MOV #PD.POR/1000,R3 ; SAVE BLOCK NUMBER IN R3 .ENDC ;EQ DU$BOO CALL CORWRT ; WRITE BLOCK 1 - READ N BCS S.IGN ; BRANCH IF ERROR .IF EQ DU$BOO MOV VALUE,(R2) ; Set the boot-time port .IFF ADD OFFSET,R2 ; Get correct offset MOV VALUE,(R2) ; Set the boot-time port .ENDC ;EQ DU$BOO CALL CORREA ; WRITE BLOCK N - READ 1 BCS S.IGN ; BRANCH IF ERROR BR S.NOR ; BRANCH IF NO ERROR .SBTTL SET VECTOR(S) ;+ ; Set code for vectors ;- SET.VEC: CMP R0,#500 ; Is this a valid vector address. BHIS S.ERR ; No, then take SET.ERR exit. .IF NE DU$PORTS-1 .ADDR #DU$VTB+BIAS,R1 ; R1 -> handler vector table. DEC R3 ; Change from 1,3,5,7 to 0,2,4,6 MOV R3,-(SP) ; Save it for later ASL R3 ; Now convert to vector table entry ADD (SP),R3 ; offset .ASSUME VT.ESZ EQ 6 ADD R3,R1 ; R1 -> specific entry. .IFF .ADDR #DUSTRT+BIAS,R1 ; R1 -> single vector entry. .ENDC ;NE DU$PORTS-1 MOV R0,@R1 ; Store the vector. .IF NE DU$PORTS-1 ADD (SP)+,R1 ; Correct for disparity in table sizes .ASSUME PC.ESZ EQ 10 ADD #PCTAB-DU$VTB+PC.VEC,R1 ; Move to the equivelent PCTAB entry. .ASSUME VT.ESZ+2 EQ PC.ESZ .IFF .ADDR #INILST+BIAS,R1 ; R1 -> directly to command. .ENDC ;NE DU$PORTS-1 ASR R0 ; Divide by 2. ASR R0 ; Divide by 4. BIS #IE,R0 ; Or in the interrupt enable. MOVB R0,@R1 ; Store the computed vector. BR S.NOR ; Take the success exit. .SBTTL ADIOS - Common exit point for overlayed code ;+ ; Common exit back to block 0 resident part ;+ ; Set code error and success exits ;- S.NOR: CLR R2 ; Indicate no error S.IGN: CMP (PC)+,(PC)+ ; SKip next; Error already set R2 S.ERR: MOV #1,R2 ; Indicate common error JMP GETBK0+BIAS ; GO TO BLOCK ZERO .SBTTL CORE - Sub/Coroutine for core reads/writes ;+ ; This routine will write block 1 to disk - read block n indicated by ; register 3 - return to caller - write block n back to disk with the ; changes the caller made - read block 1 back - return to caller. ; It is assumed that after the first call to CORWRT then modifications ; will be made and a call to CORREA is performed. ; In one case the bootstrap block is read - in another case block zero ; is read to modify the installation CSR etc. ; ; INput: R3 = block to read ; Output: R2 = buffer ; EMT area used for accessing handler file BAREA: .BYTE 17,11 ; Channel 17, write .BLKW ; Block number .BLKW ; Buffer address .WORD 256. ; Transfer length (256. words) .WORD 0 ; Wait-mode CORWRT: .ADDR #BAREA+4,R1 ; R1 -> BAREA+4 (buffer address) .ADDR #1000+BIAS,R2 ; R2 -> BUFFER ; (overwrite in-core block 1) MOV R2,(R1) ; Set the buffer address MOV #1,-(R1) ; Set up to save the current TST -(R1) ; block one as it may have been ; altered by a previous SET command MOV R1,R0 ; because we need R0 for the EMT's EMT 375 ; *** .WRITW *** BCS C2.ERR ; In case the write failed. MOV R1,R0 ; Restore R0 DECB 1(R0) ; Make it a read. MOV R3,2(R0) ; SET THE BLOCK to read EMT 375 ; *** .READW *** BCS C.ERR ; In case read fails BR C.NOR CORREA: MOV R1,R0 ; R0 -> EMT area again INCB 1(R0) ; Change it to a write MOV R3,2(R0) ; Set block to write EMT 375 ; *** .WRITW *** BCS C2.ERR ; iN CASE WRITE FAILS... MOV R1,R0 ; R0 -> EMT area (one more time...) DECB 1(R0) ; Change it back to a read MOV #1,2(R0) ; of block 1 of handler EMT 375 ; *** .READW *** BCS C.ERR ; iN CASE READ FAILS... INCB 1(R1) ; Restore EMT block to WRITE BR C.NOR C2.ERR: MOV #2,R2 ; INDICATE FAILURE BR C.NOR C.ERR: MOV #1,R2 ; INDICATE FAILURE C.NOR: RTS PC ; Return to caller .ASSUME <.-OVRBK0> LE OVRSIZ ; CHECK BOUNDARIES .SBTTL SPCDRPTR - SPecial DRPTR codes. ;+ ; This code will perform various things, ; ; o Create a global region under XM, ; o Check for Falcon, ; o Install CSR checks, ; o Attach to an existing global region. ; The advent of the .drptr macro allowed for this once only code to reside ; down here. ; ; INPUT: ; R0 -> handler routine being called. ; R1, R2 - undefined ; R3 -- type of routine being called. ; R4 -> read routine ; R5 -> $ENTRY word (handler fourth word) ;- . = OVRBK0 + BLK ; BLOCK BOUNDARY OVRBK1: .SBTTL ONCE - Load/Fetch once only code. ONCE:: MOV R2,SSLOT ; SAVE SOME OF THE PARAMETERS MOV R4,SRDRTN ; SAVE READ ROUTINE MOV R5,SENTRY ; SAVE $ENTRY PTR MOV @R5,R3 ; R3 -> ENTRY POINT HANDLER .IF EQ DU$BOO CALL FALCON ; CHECK IF WE ARE ON A FALCON .ENDC ;EQ DU$BOO .IF NE DU$PORTS-1 ; IF MORE THAN ONE PORT CALL CKPORT ; GO CHECK PORTS .ENDC ;NE DU$PORTS .IF NE MMG$T .ENABL LSB MOV @#SYSPTR,R4 ; R4 -> BASE OF RMON ; Find the global region - to get the address/32. MOV P1EXT(R4),R4 ; R4 = OFFSET TO P1EXT .ADDR #DUNAME,R5 ; R5-> RAD50 NAME DU $ GLOBAL CALL FINDGR(R4) ; GO GET THE CONTROL BLOCK BCS GETGBL ; BRANCH IF NONE MOV GR.ADR(R1),P1HIGH-DUBASE(R3) ; STORE THE ADDR/32 OF REGION MOV P1HIGH-DUBASE(R3),R1 ; R1 = ADDR/32 BR 50$ ; GO MERGE; SAVE PARAMETERS ;+ ; Part of the installation of XM will allocate a global region ; ; Memory requirements: ; ; MMG$T = 1 => 2K ; or ; ERL$G = 1 => 2K ; ADDR/32 => 40 ; ; DU$BBR = 1 => 8K ; ADDR/32 => 200 ; .IIF EQ DU$BBR MEMDUX =: 100 .IIF NE DU$BBR MEMDUX =: 200 ;- GETGBL: MOV @#SYSPTR,R4 ; R4 = BASE OF RMON MOV MEMPTR(R4),R0 ; R0 = OFFSET TO MEMORY PTR ADD R4,R0 ; R0 -> TABLE OF MEMORY PTR MOV CORPTX(R0),R5 ; R5 = OFFSET TO FREE LIST ADD R4,R5 ; R5 -> FREE LIST 20$: CMP #-1,(R5)+ ; END OF LIST ? BNE 20$ ; NO, KEEP LOOKING FOR IT! ; R5 -> Start of region control block area 30$: CMP #-1,@R5 ; END OF RCB LIST ? BEQ D.BAD ; YES, BRANCH ; FAILURE TST @R5 ; Empty entry ? BEQ 40$ ; YES, BRANCH; FOUND AN ENTRY ADD #GR.ESZ,R5 ; POINT TO NEXT ENTRY BR 30$ ; REPEAT ; R5 -> Entry RCB 40$: MOV P1EXT(R4),R0 ; R0 -> EXTERN ROUTINES MOV #MEMDUX,R2 ; R2 = SIZE/32 MEMORY MOV R3,-(SP) ; SAVE R3 CALL XALLOC(R0) ; GET MEMORY FROM FREE LIST MOV (SP)+,R3 ; RESTORE R3 BCS D.BAD ; BRANCH IF NOT OBTAINED! ; Build RCB MOV R2,(R5)+ ; STORE THE SIZE/32 MOV R1,(R5)+ ; STORE THE ADDR/32 MOV R1,P1HIGH-DUBASE(R3) ; STORE IN DRIVER TOO. MOV #GR.PVT,(R5)+ ; STORE STATUS = PRIVATE MOV #DU$NAM,(R5)+ ; STORE NAME 1ST WORD MOV #<^R$>,@R5 ; STORE NAME 2ND WORD 50$: CLR R0 ; R0 = 0 ASHC #6,R0 ; R0,R1 CONTAIN PHYSICAL ADDR MOV R0,PHYSH-DUBASE(R3) ; STORE PHYSICAL HIGH MOV R1,PHYSL-DUBASE(R3) ; STORE PHYSICAL LOW ; Go read code into upper memory CALL UPDUX ; READ PSECT DUX UP IN MEM BCS D.BAD ; BRANCH IF ERROR .DSABL LSB .ENDC ;NE MMG$T ;+ ; Common exit points ;- D.GOO: TST (PC)+ ; SKIP NEXT; CLEAR CARRY D.BAD: SEC ; SET CARRY RETURN ; RETURN TO CALLER ; Definitions DUNAME: .WORD DU$NAM ; GLOBAL NAME 1ST WORD .RAD50 /$ / ; 2ND WORD SENTRY: .WORD 0 ; $ENTRY SAVED SSLOT: .WORD 0 ; SLOT*2 SAVED SRDRTN: .WORD 0 ; READ ROUTINE SAVED .IF NE MMG$T .SBTTL UPDUX - Move psect DUX to high memory ;+ ; R0 = HIGH PHYSICAL 6 BITS ; R1 = LOW PHYSICAL 16-BITS ; R3 -> HANDLER + 6 ;- .ENABL LSB UPDUX:: ; Calculate MRING/CRING physical address MOV #XMRING,R2 ; R2 -> XMRING (VIRTUAL) $REL .-2 XMRING DUX SUB #BEGREL,R2 ; R2 = PAR1 OFFSET XMRING ADD R2,R1 ; ADD TO PHYSICAL LOW ADC R0 ; CARRY TO PHYSICAL HIGH MOV R1,MRPTR-DUBASE(R3) ; STORE LOW 16-BITS MOV R0,MRPTR+2-DUBASE(R3) ; STORE HIGH BITS ; Read Psect DUX into high memory MOV @#KISAR1,-(SP) ; SAVE MAPPING MOV P1HIGH-DUBASE(R3),@#KISAR1 ; MAP TO HIGH MEMORY MOV SENTRY,R2 ; R2 -> $ENTRY FOR DU MOV SSLOT,R0 ; R0 = SLOT*2 ASL R0 ; R0 = *2 ADD R2,R0 ; R0 = $ENTRY + *2 ADD #2,R0 ; $ENTRY + *2 + 2 MOV @R0,R0 ; R0 -> STARTING BLOCK DUX.SYS + 1 DEC R0 ; SUBTRACT -1 MOV #20000,R5 ; SAVE STARTING VIRTUAL ADDRESS ADD #DUXBAS/1000,R0 ; ADD BLOCK OFFSET TO START READ MOV #DUXSIZ,R4 ; R4 = SIZE OF PSECT DUX (BYTES) 30$: CMP R4,#BLK ; READ LESS THAN 1 BLOCK ? BGT 40$ ; YES, BRANCH MOV R4,R1 ; USE THE REMAINING BYTE COUNT BR 44$ ; SKIP NEXT 40$: MOV #BLK,R1 ; MAKE IT A BLOCK 44$: ASR R1 ; R1 = WORDCOUNT .ADDR #ONCBUF,R2 ; R2 -> BUFFER TO READ MOV R1,-(SP) CALL @SRDRTN ; READ DUX MOV (SP)+,R1 BCS 100$ ; BRANCH IF ERROR CALL STORUP ; MOVE CODE TO HIGH MEMORY ; R5 -> IS UPDATED FOR NEXT STORAGE LOCATIONS SUB #BLK,R4 ; UPDATE BYTES LEFT; ANY ? BLE 45$ ; BRANCH IF NONE INC R0 ; NEXT BLOCK BR 30$ ; MERGE ; DUX is in our region ... R3 -> HANDLER + 6 ; Init command/response area 45$: MOV #LN.CMD,R1 ; R1 -> START OF CMD $REL .-2 LN.CMD DUX MOV #P.CSIZ+4,R2 ; R2 = SIZE TO CLEAR (WORDS) 60$: CLR (R1)+ ; CLEAR WORD DEC R2 ; ANYMORE ? BNE 60$ ; NO, REPEAT ; Fix the DUR references in DUX. .ADDR #DUR.LST,R0 ;Point to rel list 10$: MOV (R0)+,R1 ; GET NEXT RELOC LIST ENTRY BEQ 20$ ; DONE THIS LIST ADD R3,@R1 ; RELOCATE VALUE IN ADDRESS BR 10$ ; AND DO NEXT ; Relocate some pointers. 20$: MOV #20000,R5 ; R5 -> VIRTUAL START MOV @#KISAR1,R1 ; SAVE HIGH MAPPING MOV (SP),@#KISAR1 ; RESTORE LOW MAPPING MOV $MPPTR-DUBASE(R3),-(SP) ; STORE $MPPTR MOV $PTWRD-DUBASE(R3),-(SP) ; STORE $PTWRD MOV $GTBYT-DUBASE(R3),-(SP) ; STORE $GTBYT .IF NE ERL$G MOV $ELPTR-DUBASE(R3),-(SP) ; STORE $ELPTR .ENDC ;NE ERL$G MOV @#SYSPTR,R2 ; R2 -> BASE OF RMON MOV P1EXT(R2),-(SP) ; STORE $P1EXT IN STACK TOO MOV (SP),$P1EXT-DUBASE(R3) ; STORE ADDRESS IN LOW MEMORY MOV R1,@#KISAR1 ; MAP BACK TO HIGH MEMORY MOV (SP)+,H$P1EX-DUXBAS(R5) ; STORE $P1EXT IN HIGH MEMORY .IF NE ERL$G MOV (SP)+,H$ELPT-DUXBAS(R5) ; STORE $ELPTR .ENDC ;NE ERL$G MOV (SP)+,H$GTBY-DUXBAS(R5) ; RELOC $GTBYT MOV (SP)+,H$PTWR-DUXBAS(R5) ; RELOC $PTWRD MOV (SP)+,H$MPPT-DUXBAS(R5) ; RELOC $MPPTR ; Relocate some data. (R1 = HIGH RELOC) .ADDR #ONCBUF,R0 ; R0 -> BUFFER AREA MOV (SP),@#KISAR1 ; RELOC BACK TO LOW MEMORY MOV #UDAIP-DUBASE,R2 ; R2 = OFFSET INTO UDAIP ADD R3,R2 ; R3 -> UDAIP MOV #LOWDATA,R4 ; R4 = BYTES TO RELOCATE ADD R4,R0 ; "no software war please"! ASR R4 ; R4 = WORDS TO RELOCATE 80$: MOV (R2)+,-(R0) ; STORE DATA IN STACK SOB R4,80$ ; REPEAT TILL FINISH MOV R1,@#KISAR1 ; MAP BACK TO HIGH MEMORY MOV #LOWDATA,R4 ; R4 = BYTES MOV #XUDAIP,R2 ; R2 -> XDUAIP $REL .-2 XUDAIP DUX ADD R4,R2 ; GO TO END OF LIST ASR R4 ; R4 = WORDS 90$: MOV (R0)+,-(R2) ; RESTORE THE DATA (BACKWARD) SOB R4,90$ ; REPEAT TILL FINISH ; Restore mapping an exit. 100$: MOV (SP)+,@#KISAR1 ; RESTORE MAPPING RTS PC .DSABL LSB .ENDC ;NE MMG$T .IF NE MMG$T ; TABLE FOR RELOCATION DUR.DID = 1 DUR.LST: .REPT DUR.CNT .Irp x <\DUR.DID> .WORD DUR'x $REL .-2 DUR'x DUX .ENDR DUR.DID = DUR.DID+1 .ENDR .WORD 000000 ; end of list .EVEN .ENDC ;NE MMG$T ;+ ; STORUP - Move code from low memory to high memory! ;- ; Input: R1 = WORDCOUNT ; R5 -> VIRTUAL ADDRESS ; R2 -> INPUT BUFFER ; Output: R5 -> NEXT LOCATIONS ;- STORUP: MOV R2,-(SP) ; SAVE INPUT BUFFER 10$: MOV (R2)+,(R5)+ ; STORE WORD DEC R1 ; ARE WE FINISH? BNE 10$ ; NO, REPEAT MOV (SP)+,R2 ; RESTORE RTS PC FBLOC:: .ASSUME . LE OVRBK1+<1*BLK> ; WE NEED SECOND BLOCK FOR BUFF ;+ ; Block align the next code. The following code will be executed and then ; phased out for allowing buffer space to read code into high memory. ;- . = OVRBK1+<1*BLK> ; START OF BUFFER BLOCK ALIGN ONCBUF:: .IF NE DU$PORTS-1 .SBTTL CKPORT - Check for installed ports ;+ ; This routine will verify that the sysgened port do indeed exist on ; the virtual hardware configuration. ;- CKPORT: .MFPS ; SAVE CURRENT PSW .MTPS #340 ; DON'T LET ANYBODY ANNOY US MOV @#INS.V,-(SP) ; STORE OLD TRAP VECTOR .ADDR #DU.INS,R5 ; R5 -> NEW TRAP ROUTINE MOV R5,@#ILG.V ; REDIRECT TO OUR NEW TRAP ; Port 1 processing INPOR1: MOV PCTAB+-DUBASE(R3),R1 ; R1 = CSR FOR PORT 1 CLC ; INDICATE NO ERROR MOV @R1,R0 ; TRY TO ACCESS IT! BCC INPOR2 ; BRANCH IF OK ; Must make port 1 invalid MOV #1,R4 ; R4 = PORT NUMBER CALL PORTUP ; UPDATE UNIT WITH THIS PORT! INPOR2: .IF NE DU$PORTS-2 MOV PCTAB+-DUBASE(R3),R1 ; R1 = CSR FOR PORT 2 CLC ; INDICATE NO ERROR MOV @R1,R0 ; TRY TO ACCESS IT! BCC INPOR3 ; BRANCH IF OK ; Must make port 2 invalid MOV #2,R4 ; R4 = PORT NUMBER CALL PORTUP ; UPDATE UNIT WITH THIS PORT! INPOR3: .IF NE DU$PORTS-3 MOV PCTAB+-DUBASE(R3),R1 ; R1 = CSR FOR PORT 3 CLC ; INDICATE NO ERROR MOV @R1,R0 ; TRY TO ACCESS IT! BCC NOPOR ; BRANCH IF OK ; Must make port 3 invalid MOV #3,R4 ; R4 = PORT NUMBER CALL PORTUP ; UPDATE UNIT WITH THIS PORT! NOPOR: .ENDC ;NE DU$PORTS-3 .ENDC ;NE DU$PORTS-2 MOV (SP)+,@#ILG.V ; RESTORE THE OLD TRAP VECTOR .MTPS ; RESTORE OLD PRIORITY RTS PC ; RETURN TO CALLER ;+ ; PORTUP - PORT update subroutine ; ; Input: R4 = port number ;- PORTUP: MOV #UTTAB-DUBASE,R1 ; POINT TO TRANSLATION TABLE OFFSET ADD R3,R1 ; R1 -> ACTUAL LOCATION MOV #8.,R0 ; R0 = COUNT OF UNITS TO CHECK 10$: CMPB R4,UT.PORT(R1) ; IS THIS PORT IN R4? BNE 20$ ; NO, BRANCH BISB #200,UT.PORT(R1) ; MARK AS INVALID! 20$: DEC R0 ; ARE WE FINISH? BEQ 30$ ; YES, GO TO NEXT PORT ADD #UT.ESZ,R1 ; POINT TO NEXT ENTRY BR 10$ ; REPEAT 30$: RTS PC ; RETURN .ENDC ;NE DU$PORTS-1 .IF EQ DU$BOO .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. ;- .IFTF DU.INS: BIS #1,2(SP) ; Set carry RTI ; and return from trap .IFT FALCON: .MFPS ; Save current PSW .MTPS #340 ; Don't let anything annoy us MOV R0,-(SP) ; Save R0 for awhile MOV @#INS.V,-(SP) ; Save current trap vector contents .ADDR #DU.INS,R5 ; Build address of our own routine MOV R5,@#INS.V ; Redirect to our trap routine CLR R0 ; Reset for later test MFPT ; Do processor determination CMP R0,#4 ; Running on a FALCON? BNE 10$ ; Nope, then no alteration needed .IF EQ DU$PORTS-1 MOV #DU$ALT,UDAIP-DUBASE(R3) ; Yes, use the alternate CSR MOV #DU$ALT+2,UDASA-DUBASE(R3) .IFF MOV #DU$ALT,PCTAB-DUBASE(R3) ; (Done for multi-port handlers also) MOV #DU$ALT+2,PCTAB+2-DUBASE(R3) .ENDC ;EQ DU$PORTS-1 10$: MOV (SP)+,@#INS.V ; Restore saved vector MOV (SP)+,R0 ; Restore saved work register .MTPS ; Restore previous priority RTS PC .ENDC ;EQ DU$BOO ONCEND:: .ASSUME . LE OVRBK1+<2*BLK> ; LIMIT OF SIZE . = OVRBK1 + <2*BLK> ; ALIGN DUX PSECT IN A BLOCK BND .END