.MCALL .MODULE .MODULE UM,VERSION=178,COMMENT=,AUDIT=NO ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only with the ; inclusion of the above copyright notice. This software, or ; any other copies thereof, may not be provided or otherwise ; made available to any other person except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. ;++ ; ; Edit Who Date Description of modification ; ---- --- ---- --------------------------- ; 001 WLD 08-MAR-90 New copyright and edit history. ; Removal of PUSH and POP macro ; invocations. ; 002 WLD 03-APR-90 Installation verification routine: ; changed ; MFPT..CMP R0,.. ; to ; MFPT..CMPB R0,.. ; 003 WLD 26-JUN-90 Included DB's changes to SET.UT ; for multi-partition DU software boot. ; Modified SYSTIN per DB's suggestions. ; 004 WLD 27-JUN-90 Corrected SET.CSR to change XUDASA ; and XUDAIP for single-ported systems. ; Fixed bug in DISPAT for 64 unit ; support. ; 005 WLD 28-JUN-90 Put UM$DU conditional in SET.UT to ; eliminate PD.POR as undefined ; global in MU link. ; 006 WLD 14-AUG-90 Removed incorrect .ASSUME on size of ; load/fetch code for unmapped DU. ; The .ASSUME after the FALCON routine ; in DU.MAC is correct. ; 007 WLD 17-DEC-90 Provided entry BEGIN1 after BEGIN to ; eliminate resetting of RETRY and to ; fix the infinit loop in recovering ; from UNIT UNAVAILABLE. ; 008 WLD 26-NOV-91 MSCP ECO 21 compliance: support ; information flag and information ; event code in error logs. ; 009 WLD 13-JAN-92 Implement the SET [NO]HBTEST option. ; If SET HBTEST (default), DU will ; return an error if the last block ; (O'177777') of a partition is accessed ; by 16 bit I/O. ; 010 WLD 25-FEB-92 Implement LBN check for 16 bit I/O. ; After an MSCP unit is successfully ; brought Online, update the table ; PSTAB according to the unit size ; returned in the Online End Message. ; PSTAB is index by RT-11 unit number ; and holds the size of each disk ; partition in blocks. ; 011 WLD 26-MAY-92 Fix non-(T)MSCP error logging in the ; LOGBAD subroutine. The low byte ; of RETRY should be the number of ; retries remaining, and the high byte ; should have the device code UM$CODe. ; 012 WLD 05-JUN-92 Fix "device register save area" pointed ; to by R2 for error logging of ; SAR Packets (see CNTERR:) and ; and End Messages (see ENDMSG:). ;-- .SBTTL ************************* .SBTTL * UM * .SBTTL ************************* .SBTTL Universal (T)MSCP Class Handler .SBTTL Edit History .ENABL LC ;+ ; ; Original version: ; ; V05 (000) 31-Oct-85 Start of coding for TMSCP/MSCP class support. ; BC Derived from CA's DU handler, old MU handler ; with inspiration from NI/NC/NQ handlers. ; ; V05 (001) 21-Dec-86 Add support for caching tapes; common source ; BC cleanup for use w/DU. ; ; 131 11/88 Add support for asynchronous datagrams, (t)mscp ; error logging, 64-unit support, and all other ; Matthew v5.5 functionality. Also add BBR datagram support ; Sacks to DU.MAC, incorporating changes and fixes found ; in Colorado. Also modifed TU.MAC accordingly. ;- ;+ ; This portion of the TMSCP/MSCP handlers contains code common to the MSCP ; disk (DU) and TMSCP tape (MU) handlers. In general, code specific to DU ; or to MU should be in the appropriate device specific module: DU.MAC for ; DU or TU.MAC for MU. There are numerous exceptions to this rule, usually ; where the device specific code is only a few instructions, or is made up ; of lots of branches, which can be difficult to move around. ;- .SBTTL Conditional Assembly Summary ;+ ;COND ; UM$DU (0) Disk support ; 0 no disk support ; 1 disk support ; ; UM$MU (0) Tape support ; 0 no tape support ; 1 tape support ; ; Select exactly one from the two above ; ; MMG$T std conditional ; TIM$T std conditional (no code effects) ; ERL$G std conditional ; UM$ERL =0 <---> ERL$G=0 ; =1 <---> ERL$G=1, i.e., old RT error logging ; =2 <---> MSCP error logging, and, ERL$G=0 ; ; UM$NAM region name (^RDU ) or (^RMU ) ; ; UM$CSR First CSR ; UM$CS1 Second CSR ; UM$CS2 Third CSR ; UM$CS3 Fourth CSR ; ; UM$VEC First vector ; UM$VC1 Second vector ; UM$VC2 Third vector ; UM$VC3 Fourth vector ; ; UM$PORts (1) ports (=DU$PORts or MU$PORts) ; ; UM$UNIts units (=DU$UNIts or MU$UNIts) ; ; For UM$DU =1 ; ; DU$BBR (0) Dynamic bad block replacement ; 0 no dynamic replacement ; 1 dynamic replacement (only if MMG$T = 1) ; ; DU$ALT Alternate CSR (FALCON) ; ; For UM$MU =1 ; ; MU$FSM (0) FSM support ; 0 no FSM support (hardware handler) ; 1 FSM support ; ; (NOP for TU81E, so conditionalized out.) ; MU$EWR (0) Enhanced Write Error Recovery support ; 0 no EWR support ; 1 EWR support ;- .SBTTL Class Handler Assembly Parameters ; Assembly parameters .IIF NDF UM$MU UM$MU = 0 .IIF NE UM$MU UM$MU = 1 .IIF NDF UM$DU UM$DU = 0 .IIF NE UM$DU UM$DU = 1 .IIF NE UM$MU DU$BBR= 0 ;Set no bad block replacement if MU .IIF NDF MMG$T MMG$T = 0 ;Set unmapped if not defined .IIF NDF UM$N64 UM$N64 = 0 ;default to no 64-unit support .IIF NE UM$MU UM$N64 = 0 ;force no 64-unit support on MU MAX$BT = 8. ;RT units 0 .. MAX$BT-1 are bootable .IIF EQ .ERROR <;NO hardware type specified> .IIF NE -1 .ERROR <;More than 1 hardware type specified> ; Additional Audit Parameters .UMGEN =: UM$PORTS!!! .UMUNI =: UM$UNI ;TU.MAC takes care of addition MU audit parameters .SBTTL Common MSCP/TMSCP Protocol Definitions ;+ ; MSCP Envelope Offsets and Masks ;- ENVLEN =: 4 ;UQSSP envelope is four bytes long PAKLEN =: -4 ;first word of envelope contains packet length ;in bytes MSGTYP =: <-2> ;message-type/credits byte MSGMSK =: 360 ;high four bits of third byte are message type ENDMES =: 0 ;code for synchronous response to a command, ;i.e., an MSCP end message DATGRM =: 20 ;code for an MSCP datagram CREDIT =: 40 ;code for an MSCP credits packet MAINTN =: 360 ;code for UQSSP maintenance protocol CONNID =: 3 ;identifies what kind of device has connection .IF NE UM$DU ;define virtual circuit identifier VIR.ID =: DISKID * 400 .ENDC ;NE UM$DU .IF NE UM$MU VIR.ID =: TAPEID * 400 .ENDC ;NE UM$MU ;+ ; Command Packet Offsets ; ; Generic Command Packet Offsets and Field Lengths (bytes): ; ; Offset Length Contents ;- 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.CSIZ =: 48. ;48. Total length of a packet .IIF EQ UM$ERL P.MSIZ =: 48. ;size of End Messages expected ;without error logging. .IIF EQ UM$ERL-1 P.MSIZ =: 48.+4 ;End Message + CSR addr/SAR contents .IIF EQ UM$ERL-2 P.MSIZ =: 128. + 4 ;Error Log + CSR addr/SAR contents B.MSIZ =: 60 ;this is just for the bootstrap's command packets ;+ ; 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 ;+ ; 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- we send the value "TO.MIN" 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 ;+ ; 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.MEDI =: 34 ;4 media identifier P.USVR =: 52 ;1 unit software number P.UHVR =: 53 ;1 unit hardware number ;+ ; 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.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 ;2 Reserved P.CSVR =: 22 ;1 controller software version P.CHVR =: 23 ;1 controller hardware version P.CNTI =: 24 ;8 Controller ID ;+ ; 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.CCD =: 21 ;COMPARE CONTROLLER DATA Command OP.ERS =: 22 ;ERASE Command OP.FLU =: 23 ;FLUSH Command OP.CMP =: 40 ;COMPARE HOST DATA Command OP.RD =: 41 ;READ Command OP.WR =: 42 ;WRITE Command OP.AVA =: 100 ;AVAILABLE Attention Message OP.DUP =: 101 ;DUPLICATE UNIT NUMBER Attention Message OP.ACP =: 102 ;ACCESS PATH Attention Message OP.END =: 200 ;End packet flag (see note below) ;+ ; Note: End packet opcodes are formed by ORing the end packet flag with the ; command opcode. For example, OP.RD!OP.END = OP.RD endcode. If the ; endcode is OP.END alone then the command was unknown. ;- ;+ ; Command Modifiers ; ; Generic Command Modifiers: ;- MD.CMP =: 40000 ;Compare MD.SEC =: 1000 ;Suppress Error Correction MD.SER =: 400 ;Suppress Error Recovery ;+ ; AVAILABLE Command Modifiers: ;- MD.ALL =: 2 ;All Class Drivers ;+ ; Unit Flags ;- UF.CMR =: 1 ;Compare Reads UF.CMW =: 2 ;Compare Writes UF.WPD =: 400 ;Write Protect (Data Safety) UF.WPH =: 20000 ;Write Protect (hardware) UF.WPS =: 10000 ;Write Protect (software) UF.WPR =: ;+ ; 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.SUB =: 40 ;Sub-code multiplier ST.SUC =: 0 ;Success ST.CMD =: 1 ;Invalid MSCP command ST.ABO =: 2 ;Command Aborted ST.OFL =: 3 ;Unit Offline ST.AVL =: 4 ;Unit available ST.MFI =: 5 ;Media Format Error ST.WPR =: 6 ;Write Protected ST.CMP =: 7 ;Compare Error ST.DAT =: 10 ;Data Error (Sub-code = Force Error) ST.CNT =: 12 ;Controller Error ST.DRV =: 13 ;Drive Error ST.INF =: 26 ;Informational (not an error) .IF EQ UM$ERL-2 ;+ ; End Message Flags ;- EF.LOG =: 40 ;Error Log Generated ;+ ; 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 L.VSER =: 44 ;4 volume serial number - disk units L.GPCT =: 44 ;4 position (object count) - tape units L.FSVR =: 50 ;1 formatter software version L.FHVR =: 51 ;1 formatter hardware version ;+ ; Error Log message Format Codes ;- FM.CNT =: 0 ;Controller Error FM.BAD =: 1 ;Memory Error FM.DSK =: 2 ;Disk Transfer Error FM.SDI =: 3 ;SDI Error FM.SDE =: 4 ;Small Disk Error FM.TPE =: 5 ;Tape Errors FM.STI =: 6 ;STI communications or command failure FM.DEL =: 7 ;STI Drive Error Log FM.FEL =: 10 ;STI Formatter Error Log FM.RPL =: 11 ;Bad Block Replacement Attempt ;+ ; Error Log Message Flags ;- LF.SUC =: 200 ;Operation successful LF.CON =: 100 ;Operation continuing LF.BBR =: 40 ;Bad Block Replacement Request LF.RCT =: 20 ;Error During Replacement LF.INF =: 2 ;Information Flag 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.HDCD =: 50 ;4 Header Code ;+ ; Bad Block Replacement Attempt Error Log Message Offsets ;- L.RPFL =: 42 ;2 Replacement Flags ; *** L.VSER =: 44 ;4 Volume Serial Number L.LBN =: 50 ;4 Bad Logical Block L.ORBN =: 54 ;4 Old Replacement Block L.NRBN =: 60 ;4 New Replacement Block L.RPEV =: 64 ;4 cause - an event code ;+ ; Bad Block Replacement Attempt "replace flags" ;- LFR.RP =: 100000 ;Replacement Attempted LFR.FE =: 40000 ;Force Error LFR.TE =: 20000 ;Tertiary Revector LFR.RF =: 10000 ;Reformat Error LFR.RI =: 4000 ;RCT Inconsistent LFR.BR =: 2000 ;Bad RBN ;+ ; Bad Block Replacement Completion Event codes ; Realize that these are Code+Subcode values ;- BB.SUC =: 24 ;bad block successfully replaced BB.OK =: 64 ;bad block verified as OK, not replaced BB.FAI =: 124 ;Replacement failure, REPLACE command failed BB.RCI =: 164 ;Replacement failure, inconsistent RCT BB.ACC =: 224 ;Replacement failure, one or more I/O ;transfers in the BBR algorithm failed BB.NRP =: 264 ;Replacement failure, no replacement block ;is available; i. e., RCT is full BB.TWO =: 324 ;Replacement failure, recursion failure, ;two successive RBN's were bad. ;+ ; Media Format Error Event Code Subcodes ; NOTE: these are Code+Subcode values ;- MF.RCI =: 405 ;RCT corrupted MF.NRP =: 445 ;No replacement block available MF.NMP =: 505 ;No multicopy protection .ENDC ;EQ UM$ERL-2 ;+ ; Command Reference Number definitions ; ; Note: The command reference number is a 32-bit field in an MSCP packet ; available for use by the implementor. The following is RT-11's ; (not storage architecture's) decision on how to use the field. ; ; The high order 16 bits are the address of the queue element for ; the current I/O request. ; The high 11 bits of the low order word are an 11 bit counter that ; increments by 1 for each successive command and which wraps around ; The lowest 5-bits of the whole 32-bit field are a code that identifies ; the command type - this command type is used for such purposes as ; distinguishing I/O requests made by the Host BBR algorithm from I/O ; requests made by a monitor's invocation of the .DRBEG code, or ; for an internal operation, and so on. ;- CRFINC =: 40 ;CRF increments by this amount, in ;order to keep the low 5 bits free BBR.IO =: 4 ;command for a Host Initiated BBR whose datagrams may be ;modified, according to the algorithm, before being logged IGN.IO =: 2 ;datagrams for this command should be ignored, not logged INT.IO =: 10 ;command for an I/O operation internal to DU, such as ;an internal ONLINE, SET CONTROLLER CHARACTERISTICS, etc. USR.IO =: 20 ;command for an I/O request passed by the monitor to DU; ;i.e., it corresponds to the RT-11 queue element .SBTTL UQSSP 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 DO.LFP =: 2 ;send last fail packet at step four ;+ ; Masks for TK50 ; ; FW revision level and controller model are available in the ASA register ; after step 4 of the 4 step initialization has been completed. ; ; The firmware rev level is in bits 0-3 ; The model is in bits 4-7 ; ;- FWREV =: 4 ;Firmware level FWREVM =: 17 ;Firmware rev level mask, lowest ;acceptable level is 4 TKMOD =: 60 ;Mask for TK50 identifier ;+ ; 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.NUM =: 10 ;The port number: 0 <= PC.NUM <= UM$PORTS-1 .IF EQ UM$ERL-2 ;NOTE!!!! Do not change the order of these fields!!!!!!!! PC.MNG =: 12 ;22-bit physical address of this port's ring ;low order, then high order PC.ADR =: 16 ;16-bit virtual address (mapped) of the ring. PC.BUF =: 20 ;the ring's set of buffers, a virtual address PC.ID =: 22 ;hold controller id., csvrsn, and chvrsn ;stuffed after the SCC in SETCC PC.VOL =: 34 ;hold volume serial number, stuffed after ;a successful ONLINE command .ENDC ;EQ UM$ERL-2 .SBTTL MACRO Definitions ;+ ; Create an MSCP command entry into the command table (by subroutine). ;- .MACRO MSCP OPCODE,TYPE JSR R5,GETCBF .WORD OPCODE .WORD TYPE .ENDM ;MSCP ;+ ; Perform contiguous action while gone for interrupt. ;- .MACRO DO THIS,THEN,THAT JSR R5,COORD .WORD THAT-.,THIS-. .ENDM ;DO ;+ ; P1EXT Macros ; These macros are used to execute a set of instructions in P1EXT - these ; instructions map the PAR1 space to another region. ; ; .P1EXT is used from LOW memory ; .HP1EX is used from HIGH memory ;- .MACRO .P1EXT NEWPAR,?LOC .IF NE MMG$T MOV NEWPAR,LOC JSR R0,@P1EXT$ .WORD LOC-. .ENDC ;NE MMG$T .MACRO .P1END .IF NE MMG$T LOC: .WORD 0 .ENDC ;NE MMG$T .ENDM .ENDM .P1EXT .MACRO .HP1EX NEWPAR,?LOC .IF NE MMG$T MOV NEWPAR,LOC JSR R0,@H$P1EX .WORD LOC-. .ENDC ;NE MMG$T .MACRO .HP1EN .IF NE MMG$T LOC: .WORD 0 .ENDC ;NE MMG$T .ENDM .ENDM .HP1EX .IF NE DU$BBR .MACRO .YP1EX NEWPAR,?LOC .IF NE MMG$T MOV NEWPAR,LOC JSR R0,@Y$P1EX .WORD LOC-. .ENDC ;NE MMG$T .MACRO .YP1EN .IF NE MMG$T LOC: .WORD 0 .ENDC ;NE MMG$T .ENDM .ENDM .YP1EX .ENDC ;NE DU$BBR .SBTTL $REL - Macro to Mark Words to Relocate in Body of Handler ;+ ; $REL ; ; Used to mark words to relocate in the body of the handler. ; $REL uses $RELF to mark words to relocate in the FETCH/LOAD once-only code. ; ; $REL LOC VALUE BASE ; ; LOC -- Location of word to relocate ; VALUE -- Value to relocate it to ; BASE -- Base relocation on (UMR, UMX, UMY, UYR, UM) ;- UMR.CNT = 0 ;UMR.CNT is the number of entries in the ;UMR Psect. The UMR psect is a list of all ;memory locations in the high memory psect UMX ;that are referencing low memory. UYR.CNT = 0 ;UYR.CNT is the number of entries in the ;UYR Psect. The UYR psect is a list of all ;memory locations in the high memory psect UMY ;that are referencing low memory. UMX.CNT = 0 ;UMX.CNT is the number references by code in ;low memory to the UMX psect in high memory. ;This use of $REL also works as a UMX psect ;PIC generator UMY.CNT = 0 ;UMY.CNT is the number references by code in ;low memory to the UMY psect in high memory. ;This use of $REL also works as a UMYpsect ;PIC generator. UMY is the psect which ;contains the DU Host Assisted BBR algorithm UM.CNT = 0 ;This is the number of low memory to low ;low memory PIC references .MACRO $REL LOC VALUE Base ;first we will do the mapped definition of $REL which has five cases ; i) base = UMX ; ii) base = UMY ; iii) base = UMR ; iv) base = UM ; v) base = UYR .IF NE MMG$T .....1 = . ;save the current location counter . = LOC ;set location counter to location that ;needs to be relocated ;this is the low memory reference to high memory UMX case ;this also works as the high memory reference to high memory PIC case .IF IDN UMX.CNT = UMX.CNT+1 .IRP x <\UMX.CNT> UMX'x: .WORD VALUE-UMXBase+BEGREL .ENDR . = .....1 .MEXIT .ENDC ;IDN ;this is the low memory reference to high memory UMY case ;this also works as the high memory (UMY) to high memory (UMY) PIC case .IF IDN UMY.CNT = UMY.CNT+1 .IRP x <\UMY.CNT> UMY'x: .WORD VALUE-UMYBase+BEGREL .ENDR . = .....1 .MEXIT .ENDC ;IDN ;this is the UMX High Memory reference to Low memory case .IF IDN UMR.CNT = UMR.CNT+1 .IRP x <\UMR.CNT> UMR'x: .WORD VALUE-UMBase . = .....1 .SAVE .PSECT UMRLST .WORD UMR'x $RELF .-2 UMR'x UMX .RESTORE .ENDR .MEXIT .ENDC ;IDN ;this is the UMY High Memory reference to Low memory case .IF IDN UYR.CNT = UYR.CNT+1 .IRP x <\UYR.CNT> UYR'x: .WORD VALUE-UMBase . = .....1 .SAVE .PSECT UYRLST .WORD UYR'x $RELF .-2 UYR'x UMY .RESTORE .ENDR .MEXIT .ENDC ;IDN ;this is the low memory to low memory case, actually the same as ;the UMR case, except that the table is psect UM instead of UMR .IF IDN UM.CNT = UM.CNT+1 .IRP x <\UM.CNT> UM'x: .WORD VALUE-UMBase . = .....1 .SAVE .PSECT UMLST .WORD UM'x $RELF .-2 UM'x UM .RESTORE .ENDR .ENDC ;IDN .ERROR ; Unknown B A S E "Base"; ;now we shall do the non-mapped definition of $REL which has only one case, ;namely generate a low memory PIC reference. .IFF ;NE MMG$T .....1 = . ;save the current location counter . = LOC ;set location counter to location that ;needs to be relocated ;this is the low memory to low memory case. ;this case IS THE ONLY case for non-mapped code UM.CNT = UM.CNT+1 .IRP x <\UM.CNT> UM'x: .WORD VALUE-UMBase . = .....1 ;restore the location counter .SAVE .PSECT UMLST .WORD UM'x $RELF .-2 UM'x UM .RESTORE .ENDR .ENDC ;NE MMG$T .ENDM $REL .SBTTL $RELF - Macro to Mark Words to Relocate in FETCH/LOAD Code ;+ ; $RELF ; ; Used to mark words to relocate in the FETCH/LOAD once-only code. IN ; other words it is a variation on $REL for use in FETCH/LOAD code. ; It is also used by $REL itself. ; ; $RELF LOC VALUE BASE ; ; LOC -- Location of word to relocate ; VALUE -- Value to relocate it to ; BASE -- Base relocation on (UMR, UMX) ; ;- .MACRO $RELF LOC VALUE Base .IF NE MMG$T .....1 = . ;save the location counter . = LOC ;set location counter to location whose ;contents need to be relocated ;this is the low memory reference to UMX high memory case ;this is also works as high memory reference to high memory PIC case .IF IDN .WORD VALUE-UMXBase+BEGREL . = .....1 .MEXIT .ENDC ;IDN ;this is the low memory reference to UMY high memory case ;this is also works as high memory reference to high memory PIC case .IF IDN .WORD VALUE-UMYBase+BEGREL . = .....1 .MEXIT .ENDC ;IDN ;this is the high memory reference to low memory case .IF IDN .WORD VALUE-UMBase . = .....1 .MEXIT .ENDC ;IDN ;this is the low memory reference to low memory case .IF IDN .WORD VALUE-UMBase . = .....1 .MEXIT .ENDC ;IDN .ERROR ; Unknown B A S E "Base"; .IFF ;NE MMG$T .....1 = . ;save the location counter . = LOC ;set location counter to location whose contents ;need to be relocated ;this is the low memory reference to low memory case, it is ;the only case in the non mapped handler .WORD VALUE-UMBase . = .....1 .ENDC ;NE MMG$T .ENDM $RELF ;+ ; PSECT Macros ;- .MACRO UMXPSECT .IF NE MMG$T .SAVE .PSECT UMX .ENDC ;NE MMG$T .ENDM UMXPSECT .MACRO UMYPSECT .IF NE MMG$T .SAVE .PSECT UMY .ENDC ;NE MMG$T .ENDM UMYPSECT .MACRO UMPSECT ;this restore assumes we are coming back from UMX/Y .IIF NE MMG$T .RESTORE .ENDM UMPSECT .SBTTL RET2LO - MACRO to Return From Mapped Region to Low Memory Routine ;+ ; ; In mapped environments, we return from the high memory region to a routine ; in low memory. Otherwise we simply transfer control to the low memory routine. ; ; To call a low memory routine A, then unconditionally transfer to a low ; memory routine B, push the address of B on the stack, then do "RET2LO A". ; ; RET2LO ;- .MACRO RET2LO DESTIN OP .IF NE MMG$T .IF NB MOV #DESTIN,-(SP) $REL .-2 DESTIN UMR .ENDC ;NB CALLR HRET .IFF ;NE MMG$T .IF B CALLR DESTIN .IFF ;B OP DESTIN .ENDC ;B .ENDC ;NE MMG$T .ENDM .SBTTL LOW2HI - MACRO to Transfer from Low Memory to High Memory Region ;+ ; LOW2HI - MACRO to Transfer from Low Memory to High Memory Region ; ; If this is XM, we transfer from low memory to a routine in the high memory ; region, saving the PAR1 value in P1LOW. If the PSECT argument is set to YES, ; the current PSECT is SAVEd and reset to UMX. ; ; If this is SB/FB, we simply transfer control to the specified routine. ; ; LOW2HI ; ;- .MACRO LOW2HI DESTIN,OP,PSECT=NO .IF NE MMG$T MOV #DESTIN,-(SP) ;Destination $REL .-2 DESTIN UMX MOV @#KISAR1,-(SP) ;Store old mapping ;HCALL will clean up after this call to P1EXT .P1EXT P1HIGH ; (Map to high memory) CALLR @#HCALL ;Go to it $REL .-2 HCALL UMX ; ... .P1END ; (End of block) .IF IDN , ;********************************************************************* UMXPSECT ;********************************************************************* .ENDC ;IDN , .IFF ;NE MMG$T .IF B CALLR DESTIN .IFF ;B OP DESTIN .ENDC ;B .ENDC ;NE MMG$T .ENDM .SBTTL GETCQE - Get Current Queue Element Address (Normal, Fake, Hi) into R5 ;+ ; GETCQE ; ; This macro gets the address of the current queue element into REG. ; ; This macro takes into account XM/non-XM, DU/MU, and FSM/non-FSM. ; ;- .MACRO GETCQE REG .NTYPE ...V2 REG .IF EQ MMG$T .IF NE UM$MU .IF NE MU$FSM MOV MUCQ,REG ;REG -> fake q. e. .IFF ;NE MU$FSM MOV UMCQE,REG ;REG -> c.q.e. .ENDC ;NE MU$FSM .ENDC ;NE UM$MU .IF NE UM$DU MOV UMCQE,REG ;REG -> c.q.e. .ENDC ;NE UM$DU .IFF ;EQ MMG$T MOV H$CQE,REG ;REG -> c.q.e. (hi copy) .ENDC ;EQ MMG$T .ENDM .SBTTL CLONE - MACRO: Clone Low Mem Data (XM); Equate XSYM=SYM (SB/FB) ;+ ; CLONE - MACRO to Clone Low Mem Data in Hi Mem (XM); Equate XSYM=SYM (SB/FB) ; ; This macro is used in XM to replicate the low memory data area in high ; memory, giving the high memory copy of the data the value, and the ; low memory copy the value. ; ; In SB/FB, it is used to equate the and values. ; ; The macro operates such that code in the SET/LOAD/FETCH part of the handler ; always references the (low memory in XM, equated to low memory ; in SB/FB), while the body of the handler always references the ; value (high memory in XM, low memory in SB/FB). ; ; CLONE symbol, ; ; Examples: ; Old code New Code ; --- ---- --- ---- ; .WORD 0,0 CLONE ,< .WORD 0,0> ; .UTTAB: .WORD 0 CLONE .UTTAB,<: .WORD 0> ; UTTBND =: . CLONE UTTBND,< =: .> ; INTS0: .WORD 0,0 CLONE INTS0,<: .WORD 0,0> ;- .IF NE MMG$T .MACRO CLONE SYMBOL,LIST .PSECT UMLORD .IF NB X'SYMBOL'LIST .IFF ;NB LIST .ENDC ;NB .PSECT UMXDAT SYMBOL'LIST .ENDM CLONE .IFF ;NE MMG$T .MACRO CLONE SYMBOL,LIST .PSECT UMLORD SYMBOL'LIST .IIF NB X'SYMBOL =: SYMBOL .ENDM CLONE .ENDC ;NE MMG$T .SBTTL RT-11 System Definitions ;+ ; Global Region Control Block (in free area). ;- CORPTX =: 4 ; Offset to extended memory block FSIZE =: 0 ; Size 32 word units FADDR =: 2 ; Address/32 of region GR.SIZ =: 0 ;Size of global region 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 =: 40 ; Don't return memory to free list GR.NAM =: 6 ;Name (2 RAD50 words) GR.ESZ =: 10. ;Size of Global Region Control Block in bytes ;+ ; PAR1 offsets and KT-11 definitions ;- .IF NE MMG$T BEGREL =: 20000 ;Beginning of PAR1 virtual addr ENDREL =: 37776 ;End of PAR1 virtual addr .IFF BEGREL =: 000000 ;so that these parameters have no effect ENDREL =: 000000 ;if not in XM .ENDC ;NE MMG$T PGSIZ =: 20000 ;Page size in bytes KISAR1 =: 172342 ;Kernel I-Space PAR1 ;+ ; Miscellaneous RT definitions ;- TRAP4 =: 4 ;Trap to 4 (illegal address) vector TRAP10 =: 10 ;Trap to 10 (illegal instruction) vector JOBMK =: 170 ;Job number mask (Q$JNUM) UNITMK =: 7 ;Unit number mask (Q$UNIT) U64MSK =: 160 ;High three bits of unit number (Q$FUNC) ;meaningful only in a 64 unit handler TBIT =: 200 ;type bit of Q$FUNC byte in queue element ;(valid only in a 64 unit handler) FNBITS =: 17 ;function type bits of Q$FUNC byte in queue ;element (valid only in a 64 unit handler) PS =: 177776 ;Processor Status Word RDCH17 =: <10*400>+17 ;Read, Channel 17 WRCH17 =: <11*400>+17 ;Write, Channel 17 .SBTTL Unit Translation Table Offsets / Miscellaneous Definitions ;+ ; Unit Translation Table Offsets ;- UT.UNIT =: 0 ;MSCP unit number (1 byte). .IF NE UM$MU UT.JOB =: 2 ;Job # Byte (1 byte). (Tape only) .ENDC ;NE UM$MU .IF NE UM$DU UT.PART =: 2 ;Disk partition to use (1 byte). (Disk only) .ENDC ;NE UM$DU UT.PORT =: 3 ;UDA port to be used (1 byte). UT.ESZ =: 4 ;Unit translation table entry size. ;+ ; Miscellaneous definitions ;- NRETRY =: 8. ;Retry initialization up to 8 times. BLK0 =: 0 ;Block zero BLK =: 1000 ;Size of a block in octal (512 bytes) VT.ESZ =: 6 ;Size of RT-11 vector table in bytes. UM$PRI =: 5 ;Device priority level ;+ ; Miscellaneous Driver Definitions ;- IN.TMO =: 200 ;INITFL flag => set controller timeout IN.ONL =: 100000 ;INITFL flag => bring unit online IOP.NR =: 177777 ;INTEOP flag => internal operation/no recovery .SBTTL .DRINS - Installation Code .ASECT . = 0 REF0:: ;Reference point zero . = 120 ;+ ; Installation code and EMT area for updating boot-time CSR ;- .IF EQ UM$PORTS-1 .DRINS UM .IFF .IF EQ UM$PORTS-2 .DRINS UM, .IFF .IF EQ UM$PORTS-3 .DRINS UM, .IFF .DRINS UM, .ENDC ;EQ UM$PORTS-3 .ENDC ;EQ UM$PORTS-2 .ENDC ;EQ UM$PORTS-1 BR DATAIN ;Data device entry .IF NE UM$MU BR I.BAD ;System device entry: no MU system .ENDC ;NE UM$MU .IF NE UM$DU BR SYSTIN ;System device entry; skip global .ENDC ;NE UM$DU .SBTTL DAT/SYSTIN - Data and System Device Installation Routines ;+ ; DON'T ALLOW INSTALLATION AS DATA DEVICE ON PRO AND KXJ11. ;- FALTYP = 4 ;Exist(falcon) = 4. We use the ;MFPT instruction below DATAIN: MOV @#$SYPTR,R4 ;R4 = base of RMON ; we would do the following more friendly code, but it takes ; up too much space ; ; MOV $CNFG2(R4),R0 ;obtain config word number 2 ; BIC #^c,R0 ;mask out the bus-type bits ; CMP #,R0 ;Running on the PRO? ; BEQ I.BAD ;Branch out with error if yes ; CMP #,R0 ;if all the bus-type bits are on, ; BEQ I.BAD ;then this is a KXJ ; ; Since the PRO bit and the KXJ bit are equal, the following is ; equivalent: ; .ASSUME EQ BIT #,$CNFG2(R4) BNE I.BAD .IF NE MMG$T MOV $MEMPT(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 P1$EXT(R4),R0 ;R0 -> PAR1 externalization routines MOV #MEMUMX,R2 ;R2 = Size/32 memory CALL $XALPT(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 #UM$NAM,(R5)+ ;Store name 1st word (^RMU , or ^RDU ) MOV #<^R$>,@R5 ;Store name 2nd word .ENDC ;NE MMG$T .IF NE UM$DU SYSTIN: CLR R0 ;GET CPU TYPE OR 0. MFPT CMPB R0,#FALTYP ;IS CPU A FALCON? .IF NE MMG$T BEQ I.BAD ;MMG$T=1: YES -- ERROR. .BR I.GOOD .IFF ;NE MMG$T BNE I.GOOD ;MMG$T=0: NO: OK TO INSTALL. TST @#DU$ALT ;FALCON: DOES ALTERNATE CSR EXIST? NOP BCS I.BAD ;NO: ERROR. .BR I.GOOD .ENDC ;NE MMG$T .ENDC ;NE UM$DU ; INSTALL CODE EXITS HERE. ; I.GOOD: TST (PC)+ I.BAD: SEC RETURN .SBTTL OV.TAB - SET Routines Dispatch Table ;+ ; This space is taken from the Installation Area -- see the ; .ASSUME below. ;- .ENABL LSB OV.TAB: .WORD SET.CSR/2 ;SET DUn/MUn CSR[m]=NNNNNN .WORD SET.RET/2 ;SET DU/MU RETRY=NNN .WORD SET.UT/2 ;SET DUn/MUn UNIT=NN .WORD SET.VEC/2 ;SET DUn/MUn VECTOR=NNN ;SET DUn/MUn VEC[m]=NNN .IF NE UM$DU .WORD SET.HBT/2 ;SET DU HBTEST .WORD 0 .WORD /2 ;SET DU NOHBTEST .ENDC ;NE UM$DU .IF NE UM$ERL .WORD SET.SUC/2 ;SET DU/MU SUCCES .WORD 0 .WORD /2 ;SET DU/MU NOSUCCES .ENDC ;NE UM$ERL .IF NE UM$MU .WORD SET.DF/2 ;SET MU[n] DEFALT=N .WORD SET.DN/2 ;SET MU[n] DENSE=N .ENDC ;NE UM$MU .ASSUME . LE 400,<;INSTALL area overflow> .SBTTL .DRTABs .ENABL LSB .PSECT SPFUNS .IF NE UM$DU .DRTAB DU,UMTAB,UTTBND-UMTAB .ENDC ;NE UM$DU .IF NE UM$MU .DRTAB MU,UMTAB,UTTBND-UMTAB .ENDC ;UM$MU .DRTAB UMS,STATU$,2 .DRTAB .ENABL LSB .SBTTL .DRSET - SET Code Tables ;+ ; The tables generated by .DRSET appear below. ; ; Note that +1 in the R3 values are necessary to prevent premature ; termination of the SET table array. ; ; The routines implemented below support the DU and UM SETtable ; options ; ; An undocumented feature of KMOVLY is essential to making this all work: ; SET routines are invoked with a CALL @R2. ;- .DRSET CSR,<0*2+1>,OV.CSR,OCT .DRSET CSR0,<0*2+1>,OV.CSR,OCT .DRSET PORT,UT.PORT+1,OV.UNT,NUM .DRSET RETRY,127.,OV.RET,NUM .DRSET UNIT,UT.UNIT+1,OV.UNT,NUM .DRSET VECTOR,<0*2+1>,OV.VEC,OCT .DRSET VEC0,<0*2+1>,OV.VEC,OCT .IF GT UM$PORTS-1 .DRSET CSR1,<1*2+1>,OV.CSR,OCT .DRSET VEC1,<1*2+1>,OV.VEC,OCT .ENDC ;GT UM$PORTS-1 .IF GT UM$PORTS-2 .DRSET CSR2,<2*2+1>,OV.CSR,OCT .DRSET VEC2,<2*2+1>,OV.VEC,OCT .ENDC ;GT UM$PORTS-2 .IF GT UM$PORTS-3 .DRSET CSR3,<3*2+1>,OV.CSR,OCT .DRSET VEC3,<3*2+1>,OV.VEC,OCT .ENDC ;GT UM$PORTS-3 .IF NE UM$DU .DRSET HBTEST,-1,OV.HBT,NO .DRSET PART,UT.PART+1,OV.UNT,NUM .ENDC ;NE UM$DU .IF NE UM$ERL .DRSET SUCCES,-1,OV.SUC,NO .ENDC ;NE UM$ERL .IF NE UM$MU .DRSET DEFALT,1,OV.DF .DRSET DENSE,2,OV.DN,NUM .ENDC ;NE UM$MU OV.RTN: OV.CSR: BR OV.DSP OV.RET: BR OV.DSP OV.UNT: BR OV.DSP OV.VEC: BR OV.DSP .IF NE UM$DU OV.HBT: BR OV.DSP NOP ;Never executed. BR OV.DSP .ENDC ;NE UM$DU .IF NE UM$ERL OV.SUC: BR OV.DSP NOP ;Never executed. BR OV.DSP .ENDC ;NE UM$ERL .IF NE UM$MU OV.DF: BR OV.DSP OV.DN: BR OV.DSP .ENDC ;NE UM$MU OV.DSP: MOV OV.TAB-OV.RTN(R2),R2 ;Get addr/2 of actual SET rtn. BR GETOVR ;BR to read and execute it. .ASSUME . LE <1000-144>,<;SET code overlaps GETOVR code> .SBTTL GETOVR - Get Overlay SET Code Routine From Disk ;+ ; GETOVR - Reads a disk overlay containing the requested SET routine and ; calculates 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 ;the following code is the code which cannot be overlaid . = 1000 - 142 ;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 CALLR @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 RDBLK: .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! .BR SET.EX .DSABL LSB ; 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:RETURN ;Return SET.END: OSIZ ==: SET.END - GETOVR ;Size of fixed code that cannot be ;overlaid .ASSUME . LE 1000,<;SET area overflow (GETOVR)> .SBTTL OVR1 - SET Code Overlay Number 1 .PSECT SETOVR ;This Psect must be block aligned. OVRBK0:: ; Define the bias BIAS ==: ;Bias due to overlay ; Define storage locations VALUE: .WORD 0 ;Value to store OFFSET: .WORD 0 ;Offset to primary driver .SBTTL SET.UT - SET UNIT, PORT, and PARTITION ;+ ; 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,#UM$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. CMP R1,#UM$UNI ;is it a valid RT-11 unit number BLO 20$ ;continue if it is JMP S.ERR ;get out if it isn't 20$: ASL R1 ;Create an index into the ASL R1 ; MSCP translation table ; and primary driver UNIT/PART/PORT ; table .ASSUME UT.ESZ EQ 4 ADD R3,R1 ;Now it points to the item. MOV R1,OFFSET ;Save offset into primary driver ; UNIT/PART/PORT table .ADDR #XUTTAB+BIAS,R1,ADD ;Add in base of MSCP translation table MOVB R0,(R1) ;Store it in the table. .IF NE UM$DU CMP OFFSET,#MAX$BT*UT.ESZ ;only units 0..max$bt-1 are in PD.POR BGE 50$ MOV R0,VALUE ;Save value to store MOV #PD.POR/1000,R3 ;Save block number in R3 CALL CORWRT ;Write block zero - Read N BCS S.IGN ;Branch if error ADD OFFSET,R2 ;(R2 -> buffer) Get correct offset MOVB VALUE,(R2) ;Set the boot-time port CALL CORREA ;Write block N - Read zero BCS S.IGN ;Branch if error 50$: BR S.NOR ;Branch if no error .IFF ;NE UM$DU BR S.NOR ;MU is not HW bootable. .ENDC ;NE UM$DU .IF NE UM$MU .SBTTL SET.DN - SET DENSITY / DEFALT ;+ ; Set code for Magtape Default and Density. ;- .ENABL LSB SET.DF: CLR R3 ;Set for default density BR 10$ ;Go do it SET.DN: CMP R0,#DN1600 ;Is this the value? BLO S.ERR ;No, take error exit. BEQ 10$ ;Yes, store the code value INC R3 ;No, bump code value CMP R0,#DN6250 ;Is this the value? BNE S.ERR ;No, take error exit. 10$: BIC #100000,R1 ;Strip the "device vs unit" flag ; and use as index into density table. CMP R1,#UM$UNITS-1 ;Insure it's a valid unit number. BHI S.ERR ;No, it's too big. .ADDR #XDNTAB+BIAS,R1,ADD ;R1 -> DNTAB MOVB R3,(R1) ;Store code value in the table. .ASSUME DN.DEN EQ 0 BISB #DN.SET,(R1) ;And turn on the 'Set Density' flag .ASSUME DN.DEN EQ 0 BR S.NOR ;Exit with success status .DSABL LSB .ENDC ;NE UM$MU .IF NE UM$DU .SBTTL SET.HBT - SET HBTEST ;+ ; Set code for allowing access to block 65,535 of a partition ;- SET.HBT:CLR R3 ;Clear the preset flag. NOP ;No, entry = SET.SUC+4. MOV R3,X$HBT+BIAS ;Set the appropriate value. BR S.NOR ;Exit with success status. .ENDC ;NE UM$DU .IF NE UM$ERL .SBTTL SET.SUC - SET SUCCESS ;+ ; Set code for success error logging. ;- SET.SUC:CLR R3 ;Clear the preset flag. NOP ;No, entry = SET.SUC+4. MOV R3,X$SUCS+BIAS ;Set the appropriate value. BR S.NOR ;Exit with success status. .ENDC ;NE UM$ERL .SBTTL SET.RET - SET RETRY ;+ ; 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,X$RETRY+BIAS ;No, then store the value. BEQ S.ERR ;Zero retries not allowed BR S.NOR ;Exit with success status. .SBTTL SET.CSR - SET CSR ;+ ; SET code for CSR(s) ; Call with: R0 = New value for CSR ; R3 = CSR index*2 + 1 (guarantees nonzero, word index) ; BIAS The beginning addr of the SET code, the beginning of ; the 2-block USR disk buffer. ; BLK The length of a disk block. ; BIAS+BLK The beginning addr of block 1 in the disk buffer. ;- SET.CSR: ; Make sure we have a believable CSR value CMP R0,#160000 ;Is this a valid CSR address? BLO S.ERR ;No, then take the error exit. ; Save input values so we can use the registers MOV R0,VALUE ;Store the csr - to free R0 MOV R3,OFFSET ;Save MSCP table index. ; The information we want is in Block 0 of this file ; Select Block 0, write current block, read block 0 MOV #BLK0,R3 ;Read block zero CALL CORWRT ;Write block 1 - read n BCS S.IGN ;Branch if error! ; Un-bias the CSR index, so we can recognize CSR 0 (Installation CSR) 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+BLK ;Set the installation CSR ; Get the offset, negate for index into 'reverse' table; store VALUE 10$: MOV OFFSET,R0 ;Make a copy of the offset NEG R0 ;Negate it for reversed CSR table .ADDR #DISCSR+BIAS+BLK,R0,ADD ;Calculate pickly MOV VALUE,(R0) ;Now set the display CSR in question ; Select Block 0, write it out, read back previous block (Block 1) MOV #BLK0,R3 ;Write block n = 0 CALL CORREA ;Write block n - read 1 BCS S.IGN ;Branch if error ; Form the offset into the port control tables MOV OFFSET,R3 ;Get index into MSCP tables from ;.DRSET parameter. It's already ;multiplied by 2. .IF EQ UM$ERL-2 ; If we are doing MSCP error logging .ASSUME PC.ESZ EQ 40 ; ....each entry is 40 bytes long ASL R3 ; *4 ASL R3 ; *8 ASL R3 ; *16. ASL R3 ; *32. .IFF ;EQ UM$ERL-2 ; If we are not doing MSCP error logging .ASSUME PC.ESZ EQ 12 ; ....each entry is 12 bytes long MOV R3,-(SP) ; *2 ASL R3 ; *4 ASL R3 ; *8 ADD (SP)+,R3 ; *10. .ENDC ;EQ UM$ERL-2 ; Fix CSR addrs in XPCTAB - It's in block 1, so we don't have to read it in .ADDR #BIAS+XPCTAB,R1 ; Get buff addr + table addr ADD R3,R1 ; Add entry offset in table .ASSUME PC.AIP EQ 0 ; Polling reg must be (0) in entry MOV VALUE,(R1) ; Store the new polling reg addr .ASSUME PC.ASA EQ 2 ; Status reg must be (2) in entry MOV (R1)+,(R1) ; Store the new status reg addr ADD #2,(R1) ; Correct status reg = poll reg + 2 .IF EQ UM$PORts - 1 ;For single ported systems: ; Set new CSR addresses into XUDAIP and XUDASA -- both in block 1. .ADDR #BIAS+XUDAIP,R1 ;Get addr of XUDAIP in blk 1 buffer. MOV VALUE,@R1 ;Set new CSR address. .ASSUME XUDASA EQ XUDAIP+2 MOV (R1)+,@R1 ;Set new CSR address + 2 into ADD #2,@R1 ;XUDASA. .ENDC ;EQ UM$PORts - 1 .IF NE UM$DU MOV #PD.CSR/BLK,R3 ; Get Blk number of PORT/CSR table CALL CORWRT ; Read it BCS S.IGN ; Error, ...... ADD OFFSET,R2 ; Add offset in table of CSRn MOV VALUE,(R2) ; Store new CSR at offset in block CALL CORREA ; Write block n - Read 1 BCS S.IGN ; Branch if error .ENDC ;NE UM$DU BR S.NOR ; Branch if no error .SBTTL SET.VEC - 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 UM$PORTS-1 .ADDR #UM$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 ;NE UM$PORTS-1 .ADDR #UMSTRT+BIAS,R1 ;R1 -> single vector entry. .ENDC ;NE UM$PORTS-1 MOV R0,(R1) ;Store the vector. .IF NE UM$PORTS-1 ;calculate (port number) * (PC.ESZ) .IF EQ UM$ERL-2 .ASSUME PC.ESZ EQ 40 .ASSUME VT.ESZ+32 EQ PC.ESZ MOV (SP)+,R3 ;R3 = 2 * (port number) ASL R3 ;*4 ASL R3 ;*8 ASL R3 ;*16. ASL R3 ;*32. .IFF ;EQ UM$ERL-2 .ASSUME PC.ESZ EQ 12 .ASSUME VT.ESZ+4 EQ PC.ESZ MOV (SP)+,R3 ;R3 = 2 * (port number) ASL R3 ;R3 = 4 * (port number) .ENDC ;EQ UM$ERL-2 .ADDR #XPCTAB+PC.VEC+BIAS,R1 ADD R3,R1 ;Move to the equivalent PCTAB entry. .IFF ;NE UM$PORTS-1 .ADDR #XINILS+BIAS,R1 ;R1 -> directly to INIT parameter .IFTF ;NE UM$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. .IFF ;NE UM$PORTS-1 ADD #XPCTAB-XINILS+PC.VEC,R1;do PCTAB entry for port 0 MOVB R0,@R1 .ENDC ;NE UM$PORTS-1 .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 ;No error ; S.IGN ;Error already set R2 ; S.ERR ;Common error ;- S.NOR: CLR R2 ;Indicate no error BR S.IGN ;Skip next S.ERR: MOV #1,R2 ;Indicate common error S.IGN: CALLR 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. ; ; *** R1 MUST NOT BE MODIFIED between calls to CORWRT and CORREA. *** ; *** R2 MUST NOT BE MODIFIED between calls to CORWRT and CORREA. *** ; ; Input: R3 = block to read ; ; Output: C bit= Clear => success ; R2 = buffer ; ; C bit= Set => error ; R2 = 2 => write failed ; = 1 => read failed ;- ;EMT area for accessing handler file BAREA: .BLKW ; Channel 17, write (or read!) .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) MOV R1,R0 ;We need R0 for the EMT's .ADDR #BLK+BIAS,R2 ;R2 -> BUFFER (overwrite incore blk 1) MOV R2,@R0 ;Set the buffer address MOV #1,-(R0) ;Set up to save the current MOV #WRCH17,-(R0) ; block one as it may have been ; altered by a previous SET command EMT 375 ;*** .WRITW CHANNEL 17 *** BCS C2.ERR ;In case the write failed. MOV R1,R0 ;Restore R0 ->BAREA+4 MOV R3,-(R0) ;Set the block to Read MOV #RDCH17,-(R0) ;Make it a read. EMT 375 ;*** .READW CHANNEL 17 *** BCS C.ERR ;In case read fails BR C.NOR CORREA: MOV R1,R0 ;R0 -> BAREA+4 (EMT area) again MOV R3,-(R0) ;Set block to write MOV #WRCH17,-(R0) ;Change it to a write EMT 375 ;*** .WRITW *** BCS C2.ERR ;In case write fails... MOV R1,R0 ;R0 -> BAREA+4 (EMT area) 1 mo' time.. MOV #1,-(R0) ;Set to block 1 of handler MOV #RDCH17,-(R0) ;Change it back to a read EMT 375 ;*** .READW *** BCS C.ERR ;In case read fails... BR C.NOR C2.ERR: MOV #2,R2 ;Indicate write failure BR C.NOR C.ERR: MOV #1,R2 ;Indicate read failure C.NOR: RETURN ;Return to caller .ASSUME <.-OVRBK0> LE OVRSIZ,<;SET overlay overflow> .SBTTL ONCE - Special .DRPTR Load/Fetch Once-Only Code. ;+ ; ONCE - Special .DRPTR Load/Fetch Once-Only Code. ; ; o Creates a global region under XM, ; o Checks for Falcon, ; o Does INSTALL CSR checks, ; o Attaches to an existing global region. ; o Enables abort entry point, once hi region is set up. (Magtape only) ; ; The advent of the .DRPTR macro allows this once only code to reside here. ; ; INPUT: R0 -> handler routine being called. ; R1 -- undefined ; R2 -- SLOT*2 (SLOT is # of this device in monitor tables) ; R3 -- type of routine being called. ; R4 -> read routine ; R5 -> $ENTRY table entry for this handler ;- .ENABL LSB . = OVRBK0 + <1*BLK> ;Block boundary OVRBK1:: LDBUF0:: ONCE:: MOV R2,(PC)+ ;Save some of the parameters SSLOT: .WORD 0 ; SLOT*2 saved MOV R3,(PC)+ ;Save routine-type code SRTYPE: .WORD MOV R4,(PC)+ ;Save read routine SRDRTN: .WORD 0 ; Read routine saved MOV R5,(PC)+ ;Save $ENTRY ptr SENTRY: .WORD 0 ; $ENTRY saved MOV @R5,R3 ;R3 -> entry point handler ;for calls to UMRELO,CKPORT,and FALCON .IF NE UM$DU .IF EQ MMG$T CALL FALCON ;Check if we are on a FALCON .ENDC ;EQ MMG$T .ENDC ;NE UM$DU .IF NE UM$PORTS-1 ;If more than one port CALL CKPORT ;Go check ports .ENDC ;NE UM$PORTS-1 CALL UMRELO ;relocate root to root references MOV SENTRY,R5 ;restore $ENTRY pointer to R5 .IF NE UM$DU .IF NE UM$N64 MOV SSLOT,R2 ;restore SLOT*2 to r2 MOV SRTYPE,R3 ;restore the routine-type code to r3 CALL UNI$FX ;make appropiate 64-units adjustments .ENDC ;NE UM$N64 .ENDC ;NE UM$DU MOV @R5,R3 ;R3 -> entry point handler, for the ;calls to ONCEXM and UPUMX and RNGSET .IF EQ MMG$T CALL RNGSET ;calculate physical address of ;ring descriptors .IFF ;EQ MMG$T CALL ONCEXM ;Jump to rest of code in buffer! BR D.BAD ; Return here if error CALL RNGSET ;calculate physical address of ring ;descriptors ;read in load-code overlay to LDBUF1 MOV SENTRY,R2 ; $ENTRY entry MOV SSLOT,R0 ; $SLOT*2 ASL R0 ; $SLOT*4 ADD R2,R0 ; Point to handler disk block ADD #2,R0 ; account for extra word in $ENTRY MOV @R0,R0 ; get handler's 2nd block no. DEC R0 ; get handler's 1st block no. ADD #LDOVR/BLK,R0 ; block number of LDOVR MOV #BLK/2,R1 ; word count .ADDR #LDBUF1,R2 ; load address CALL @SRDRTN ; call system read routine BCS D.BAD ;these two moves are done here for UPSET ;they are done here because UPSET is in an overlay MOV SENTRY,R2 ;R2 -> $ENTRY for UM MOV SSLOT,R0 ;R0 = SLOT*2 ;Go read code into upper memory MOV SRDRTN,-(SP) ;pass address of read routine on stack CALLR LDBUF0+1000 ; == CALL UPUMX ; BCS D.BAD .ENDC ;NE MMG$T .BR D.GOO ;+ ; Common exit points ;- .IF EQ MMG$T D.GOO: TST (PC)+ D.BAD: SEC RETURN ;return to caller .IFF ;EQ MMG$T D.GOO: CLC BR 10$ D.BAD: MOV SENTRY,R1 ;we must return the UMRs we allocated SUB SSLOT,R1 ;if the handler didn't load MOV #$SYPTR,R3 MOV $H2UB(R3),R3 CALL UB.RLS(R3) SEC 10$: RETURN ;Return to caller .ENDC ;NE MMG$T ; Definitions UMNAME: .WORD UM$NAM ;Global name 1st word: ^R'MU 'or 'DU ' .RAD50 /$ / ;2nd word .DSABL LSB .SBTTL UNLOAD - unload and release code ;+ ; UNLOAD - the unload and the release code ;- UNLOAD:: .IF NE UM$N64 CALL UNL$64 ;restore monitor tables, etc. .ENDC ;NE UM$N64 .IF NE MMG$T ;now return the two unibus mapping registers ;we are relying on the R5 and R2 provided by the monitor when it calls ;unload code. MOV R5,R1 ;make R1 point to the handler's $PNAME SUB R2,R1 ;entry by subtracting $SLOT*2 off the ;$ENTRY pointer MOV @#$SYPTR,R4 MOV $H2UB(R4),R3 CALL UB.RLS(R3) ;call the UB release routine .IF NE UM$MU .IF NE MU$FSM MOV @R5,R1 ;get handler base ADD #,R1 ; Point to FSM's ID MOV @#$SYPTR,R4 MOV $H2UB(R4),R3 CALL UB.RLS(R3) ;release FSM's UMR .ENDC ;NE MU$FSM .ENDC ;NE UM$MU CLR R0 ;Ensure clean UNLOAD .ENDC ;NE MMG$T RETURN .IF NE UM$DU .IF NE UM$N64 .SBTTL UNI$FX - make appropriate 64-unit adjustments (64 UNITS ONLY!!!) ;+ ; UNI$FX - fix pointers in $OWNER table so as to support up to 64 units. ;- UNI$FX::MOV R0,-(SP) MOV R1,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV R5,-(SP) MOV @#$SYPTR,R0 ;does the running monitor support BIT #CF3.64,$CNFG3(R0) ;extended unit handlers? BEQ 10$ ;If not, branch and get out BIT #CF3.OW,$CNFG3(R0) ;is there an ownership table? BEQ 10$ ;if branch if not ;set up $SLOT*5 for later reference MOV R2,R3 ;R3 := $SLOT*2 ASL R3 ASL R3 ;R3 := R3*4 ADD R2,R3 ;R3 = $SLOT*5 CALL FIXOWN ;put address of extended ownership table ;into $OWNER+2, and put the address ;$OWNER+2 in R1 ;Zero out the first word of the extended ownership table ;which had a one letter device name in it CLR @(R1)+ 10$: CLC MOV (SP)+,R5 MOV (SP)+,R4 ;restore registers MOV (SP)+,R3 MOV (SP)+,R1 MOV (SP)+,R0 RETURN .SBTTL UNL$64 - additional unload code for 64 unit handler (64 units only!!) ;+ ; UNL$64 - additional unload code for 64 unit handler ; ; For a 64 unit handler, the second word of the $OWNER table needs to ; to be zeroed at unload time ;- UNL$64::MOV @#$SYPTR,R0 ;R0 -> base of RMON BIT #CF3.64,$CNFG3(R0) ;64-unit support in running monitor BEQ 10$ ;branch if not extended unit support BIT #CF3.OW,$CNFG3(R0) ;is there an ownership table? BEQ 10$ ;branch if not ;obtain $SLOT*5 MOV R2,R3 ;R3 := $SLOT ASL R3 ASL R3 ADD R2,R3 ;R3 = $SLOT*5 CALL FIXOWN ;R1 -> $OWNER+2 CLR @R1 10$: RETURN .SBTTL FIXOWN - insert pointer to extended ownership table into $OWNER ;+ ; FIXOWN - insert pointer to extended ownership table into second word ; of $OWNER table (64 UNITS AND (BIT #CF3.OW,$CNFG3) ONLY!!!) ; ; INPUT ; R2 = $SLOT*2 ; R3 = $SLOT*5 ; R5 -> $ENTRY entry for this handler ; UM$X64: extended ownership table ; ; OUTPUT ; $OWNER+2 points to extended ownership table ; R1 points to $OWNER+2 ;- FIXOWN::MOV @#$SYPTR,R1 ;R1 -> $RMON MOV $PNPTR(R1),-(SP) ADD R1,@SP ;@SP -> beginning of $PNAME ADD R2,@SP ;@SP -> beginning of $ENTRY MOV R5,R1 ;R1 -> $ENTRY entry for this handler SUB (SP)+,R1 ;R1 = byte offset into $ENTRY ADD R5,R1 ;R1 = $ENTRY + double-word index SUB R3,R1 ;R1 -> $OWNER of this handler + 8. CMP -(R1),-(R1) ;R1 -> $OWNER of this handler + 4 MOV @R5,-(R1) ;move addr of UMLQE into $OWNER+2 ADD #UM$X64-UMLQE,@R1 ;make $OWNER (pic) to point to ;extended ownership table RETURN .ENDC ;NE UM$N64 .ENDC ;NE UM$DU .SBTTL UMRELO - Relocate address references in Root to Root ;+ ; UMRELO - relocate address references in root to root. UM.LST is a ; list of all the addresses that need to be relocated ; ; On input: R3 -> to Handler + 6 ;- UMRELO::MOV R0,-(SP) MOV R1,-(SP) .ADDR #UM.LST,R0 MOV #LMSIZE/2,R1 BEQ 20$ 10$: ADD R3,@R0 ADD R3,@(R0)+ SOB R1,10$ 20$: MOV (SP)+,R1 MOV (SP)+,R0 RTS PC SOVRSZ == . - OVRBK1 ;Size of code in this overlay .IF NE MMG$T .SBTTL UMR.LST - Table for Relocating Root References from UMX (XM Only) ;********************************************************************* .PSECT UMRLST ;********************************************************************* ;+ ; UMR.LST - Table for relocating root references from UMX. ; Relocates references like: "$REL .-2 FOO UMR" ; ; See the $REL definition ;- UMR.LST:: .SBTTL UMY.LST - Table for Relocating Root References from UMY(XM Only) ;********************************************************************* .PSECT UYRLST ;********************************************************************* ;+ ; UYR.LST - Table for relocating root references from UMY. ; Relocates references like: "$REL .-2 FOO UMR" ; ; See the $REL definition ;- UYR.LST: .SBTTL LDOVR - Load code overlay ;********************************************************************* .PSECT LDOVR ;********************************************************************* LDOVR:: .SBTTL UPUMX - Move Psects UMXDAT, UMDATA, UMX to High Memory (XM Only) ;+ ; UPUMX - Move Psects UMXDAT, UMDATA, UMX to High Memory (XM Only) ; ; This routine is executed, as part of the FETCH/LOAD process, out of the ; USR buffer. Thus it is guaranteed to be not in a PAR1 space, but it must ; be careful of accessing locations in the resident handler in PAR1. ; ; Because we have only two blocks of space in the USR, this code is an ; overlay named LDOVR. LDOVR is read into LDBUF1, the second of the two ; blocks read into the USR; then we jump to it. UPUMX begins with the ; first word of LDOVR. UPUMX uses LDBUF0 as a buffer for reading the actual ; handler into high memory, block by block. Once the handler is resident, ; the relocation work (according to the relocation lists created by $REL) ; is performed. Note that the relocation lists (UMRLST, UYRLST) must be ; in LDOVR, the block that is read in. ; ; Input: R0 = $SLOT*2 ; R2 -> $ENTRY entry for hander ; R3 -> Handler + 6 ; (SP) = address of system read routine ;- .ENABL LSB UPUMX:: .ASSUME LDOVR EQ UPUMX MOV (SP)+,RDRTN2 MOV R0,SSLOT2 ;save these for later MOV R2,SENTR2 CALL UPSET ;Setup for the copy to high mem MOV @#KISAR1,-(SP) ;Save mapping MOV P1HIGH-UMBASE(R3),@#KISAR1 ;Map to high memory MOV R3,-(SP) ;protect R3 CLR R3 ;R3 will count the number of times we ;need to adjust the PAR1 value 10$: MOV #BLK,R1 ;Prepare to read 1 block. CMP R4,R1 ;Read less than 1 block ? BGT 20$ ;Yes, branch MOV R4,R1 ;Use the remaining byte count 20$: SUB R1,R4 ;Update bytes left. ASR R1 ;R1 = wordcount MOV PC,R2 SUB #<<.-LDOVR>+BLK>,R2 ;R2 -> buffer to read MOV R1,-(SP) ;protect R1 from system read routine CALL @RDRTN2 ;Read UMXDAT, UMX MOV (SP)+,R1 ;*C* BCC 30$ JMP 200$ ;jump if error 30$: CMP #40000,R5 ;must make sure that R5, initialized BHI 40$ ;by UPSET to 20000, remains a PAR1 SUB #20000,R5 ;address. ADD #200,@#KISAR1 ;adjust the PAR1 value and remember INC R3 ;that we did by incrementing a counter 40$: MOV (R2)+,(R5)+ ;Store word SOB R1,30$ ;Loop till finished ; R5 -> is updated for next storage locations INC R0 ;Next block TST R4 ;Any more to read? BNE 10$ ;Branch if more 50$: DEC R3 ;undo any changes we did to the BMI 60$ ;PAR1 value SUB #200,@#KISAR1 BR 50$ 60$: MOV (SP)+,R3 ;restore R3 to handler + 6 .BR 70$ ;+ ; UMX is in our region ... R3 -> HANDLER + 6 ... Init command/response area. ;- ACTION!!!!!!!!!! is this actually neccessary 70$: MOV #LN.CMD,R1 ;R1 -> start of cmd bufr area -4 $RELF .-2 LN.CMD UMX ; ... MOV #P.CSIZ+4,R2 ;R2 = size to clear (words) 80$: CLR (R1)+ ;Clear word SOB R2,80$ ;Loop ;+ ; Fix the UMR references in UMX. ;- MOV #UMRLSZ/2,R2 ;Get size of UMR.LST BEQ 100$ .ADDR #UMR.LST,R0 ;R0-> list of addrs of wds to relocate 90$: ADD R3,@(R0)+ ;Relocate word that address points to SOB R2,90$ ;And loop till done ;+ ; Fix the UMR references in UMY. ;- 100$: MOV #UYRLSZ/2,R2 ;Get size of UYR.LST BEQ 120$ MOV @#KISAR1,-(SP) MOV 2(SP),@#KISAR1 ;map to low memory in order to obtain ;the UMY PAR1 value MOV XP1UMY-UMBASE(R3),@#KISAR1 .ADDR #UYR.LST,R0 ;R0-> list of addrs of wds to relocate 110$: ADD R3,@(R0)+ ;Relocate word that address points to SOB R2,110$ ;And loop till done MOV (SP)+,@#KISAR1 .BR 120$ ;+ ; Relocate some pointers and data. ;- 120$: MOV #20000,R5 ;R5 -> virtual start MOV @#KISAR1,R1 ;Save high mapping MOV @SP,@#KISAR1 ;Restore low mapping 130$: MOV $MPPTR-UMBASE(R3),-(SP) ;Store $MPPTR MOV $PTWRD-UMBASE(R3),-(SP) ;Store $PTWRD MOV $GTBYT-UMBASE(R3),-(SP) ;Store $GTBYT .IF NE UM$ERL MOV $ELPTR-UMBASE(R3),-(SP) ;Store $ELPTR .ENDC ;NE UM$ERL .IF NE UM$MU ;If Magtape MOV $RLPTR-UMBASE(R3),-(SP) ;Store $RLPTR .ENDC ;NE UM$MU MOV @#$SYPTR,R2 ;R2 -> base of RMON MOV P1$EXT(R2),-(SP) ;Store P1EXT$ in stack too MOV @SP,P1EXT$-UMBASE(R3) ;Store address in low memory .IF NE DU$BBR MOV XP1UMY-UMBASE(R3),@#KISAR1 ;Map to UMY psect MOV @SP,Y$P1EX-UMYBAS(R5) .ENDC ;NE DU$BBR MOV R1,@#KISAR1 ;Map back to UMX psect MOV (SP)+,H$P1EX-UMXBAS(R5) ;Store P1EXT$ in high memory .IF NE UM$MU ;If Magtape MOV (SP)+,H$RLPT-UMXBAS(R5) ;Reloc $RLPTR .ENDC ;NE UM$MU .IF NE UM$ERL MOV (SP)+,H$ELPT-UMXBAS(R5) ;Store $ELPTR .ENDC ;NE UM$ERL MOV (SP)+,H$GTBY-UMXBAS(R5) ;Reloc $GTBYT MOV (SP)+,H$PTWR-UMXBAS(R5) ;Reloc $PTWRD MOV (SP)+,H$MPPT-UMXBAS(R5) ;Reloc $MPPTR ; Relocate some data. (R1 = High Reloc) MOV PC,R0 SUB #<.-LDOVR>,R0 ;R0 -> buffer area which will be used ;like a stack - it is the preceeding ;memory block MOV @SP,@#KISAR1 ;Reloc back to low memory MOV #XUDAIP-UMBASE,R2 ;R2 = offset into XUDAIP ADD R3,R2 ;R2 -> XUDAIP MOV #LOWDATA/2,R5 ;R5 = words to move up to high mem 140$: MOV R5,R4 ;R4 = words to move in next interation CMP #BLK/2,R4 ;Is R4 less than or equal to 256.? BHIS 150$ ;branch if yes, otherwise MOV #BLK/2,R4 ;R4 = 256. 150$: MOV R4,-(SP) ;protect word count 160$: MOV (R2)+,-(R0) ;Store data in stack SOB R4,160$ ;Repeat till finish MOV (SP)+,R4 ;restore R4 to use it again MOV R1,@#KISAR1 ;Map back to high memory 170$: MOV #UDAIP,R2 ;R2 -> place to pop stack into $RELF .-2 UDAIP UMX ;this MOV instruction will be ADD R4,R2 ;modified below. ADD R4,R2 ;We are making R2 the high mem loc. ADD R4,170$+2 ;in which to start popping code. ADD R4,170$+2 ;(R4 is word count, not a byte count) 180$: MOV (R0)+,-(R2) ;Restore the data (backward) SOB R4,180$ ;Repeat till finish SUB #BLK/2,R5 ;subtract 256. from total number of BLE 190$ ;words to move. Branch if done. MOV @SP,@#KISAR1 ;otherwise, map back to low memory and BR 140$ ;continue copying code up to high mem. 190$: .IF NE UM$DU ; Set up the displacement and PAR1 relocation bias for SEEKIT ; to be used on SEEKs MOV #SEEKIT,R4 ;R4 -> SEEKIT $RELF .-2 SEEKIT UMX MOV R4,R2 ;Save orig value in R2 BIC #17700,R4 ;Clear out mid 7 bits MOV R4,@#SEKBUF ;Use as Q.BUFF value in q-element $RELF .-2 SEKBUF UMX ASL R2 ;Shift over ASL R2 ;Two bits SWAB R2 ;Move bits into low word BIC #177600,R2 ;Clear out all but low 7 bits ADD R1,R2 ;Add in DUXBAS MOV R2,@#SEKPAR ;Use as Q.PAR value in q-element $RELF .-2 SEKPAR UMX .ENDC ;NE UM$DU CLC ; Restore mapping and exit. 200$: MOV (SP)+,@#KISAR1 ;*C* Restore mapping BCS 220$ .IF NE UM$MU ;If magtape MOV #NOP,UM$ABRT-UMBASE(R3) ;*C* Enable abort entry point now. .ENDC ;NE UM$MU 210$: RETURN 220$: MOV SENTR2,R1 ;if load/fetch failed, then we should SUB SSLOT2,R1 ;release the Unibus Mapping Registers MOV #$SYPTR,R3 ;we have allocated before returning MOV $H2UB(R3),R3 ;error. CALL UB.RLS(R3) SEC BR 210$ RDRTN2: .WORD SSLOT2: .WORD SENTR2: .WORD ;+ ; UPSET - Setup to Move Psects UMXDAT, UMDATA, UMX to High Memory (XM Only) ; ; This routine resides in the buffer, to allow for more code and tables ; in the first block. This routine is called by UPUMX to do some of the ; address calculations for the move of psects UMXDAT, UMDATA, UMX to high ; memory. It is destroyed once UPUMX starts reading blocks of psects ; UMXDAT, UMDATA, UMX into this buffer. ; ; Input: ; R2 -> $ENTRY for UM ; R0 = SLOT*2 ; ; Output: ; R0 = block # where UMXDAT, UMDATA, UMX psects begin on system device ; R1,R2 = undefined ; R3 -> Handler + 6 ; R4 = size of UMXDAT+UMDATA+UMX+UMXPAD+UMY psects (bytes) ;- UPSET: ;Setup to Read Psects UMXDAT, UMDATA, UMX into high memory 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 #BEGREL,R5 ;Save starting virtual address ADD #UMXBAS/BLK,R0 ;Add block offset to start read MOV #XMLEN,R4 ;R4 = Size of XM psects RETURN ;Get out of buffer before we read!! ;********************************************************************* .PSECT SETOV1 ;Rest of INSTALL overlay code ;********************************************************************* ;+ ; The following code is block aligned via the 'pad' PSECT UMNDLS. This ; code will be executed and then phased out, allowing buffer space ; to read code into high memory. ;- LDBUF1:: .ENDC ;NE MMG$T .IF NE UM$PORTS-1 ;For next page .SBTTL CKPORT - Check for Installed Ports ;+ ; This routine will verify that the sysgened ports do indeed exist on the ; current hardware configuration. ; ; **NOTE** Ports are numbered 0,1,2,3. Max number of ports (UM$PORTS) is 4. ; This routine is only assembled if support for two or more ports was included ; at SYSGEN time. The monitor (KMON or BSTRAP) always checks that the CSR for ; the first port (port 0) exists on the system. We start checking at port #1. ; ; Input: ; R3 -> handler entry point ; Output: ; R3 -> handler entry point ;- .ENABL LSB CKPORT::.MFPS ;Save current PS .MTPS #340 ;Don't let anybody annoy us MOV @#TRAP4,-(SP) ;Store old TRAP vector .ADDR #UM.INS,R5 ;R5 -> new TRAP routine MOV R5,@#TRAP4 ;Redirect to our new TRAP MOV R3,R5 ;R5 -> handler entry point ADD #XPCTAB-UMBASE,R5 ;R5 -> CSR for port 0 (checked by MON) MOV #1,R4 ;R4 = # of port to check (start w/ #1) 10$: ADD #PC.ESZ,R5 ;R5 -> CSR for next port MOV @R5,R1 ;R1 = CSR for next port CLC ;Indicate no error MOV @R1,R0 ;Try to access it! BCC 20$ ;Branch if ok ; Must make this port invalid (R4 = port number) CALL PORTUP ;Update units with this port! 20$: INC R4 ;Bump port number to next port CMP R4,#UM$PORTS ;Checked 'em all? BNE 10$ ;If NE, no, keep going MOV (SP)+,@#TRAP4 ;Restore the old TRAP vector .MTPS ;Restore old priority RETURN ;Return to caller .DSABL LSB .SBTTL PORTUP - PORT Update Subroutine ;+ ; PORTUP - PORT update subroutine ; ; Input: R3-> handler entry point ; R4 = port number ;- PORTUP::MOV #XUTTAB-UMBASE,R1 ;Point to translation table offset ADD R3,R1 ;R1 -> actual location MOV #UM$UNITS,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 finished? BEQ 30$ ;Yes, go to next port ADD #UT.ESZ,R1 ;Point to next entry BR 10$ ;Repeat 30$: RETURN ;Return .ENDC ;NE UM$PORTS-1 UM.INS: BIS #1,2(SP) ;Set carry RTI ; and return from trap .IF NE MMG$T ;If XM .SBTTL ONCEXM - Continuation of ONCE Routine for Extended Memory ;+ ; ONCEXM - Continuation of ONCE routine for Extended Memory ; ; This routine will find or allocate a global region in extended memory ; so that the code from psects UMXDAT, UMDATA, UMX can be read into it. ;- .ENABL LSB ONCEXM::MOV @#$SYPTR,R4 ;R4 -> base of RMON ;Find global region, get address/32. MOV P1$EXT(R4),R4 ;R4 = offset to P1EXT .ADDR #UMNAME,R5 ;R5-> Name global ^R/MU $ /or/DU $ / CALL $FGRPT(R4) ;Go get the control block .IF NE UM$MU ;If magtape BCS XD.BAD ;Branch if none MOV GR.ADR(R1),R1 ;R1 = addr/32 of region .BR 40$ ;Go merge; save parameters .ENDC ;NE UM$MU .IF NE UM$DU ;If disk BCS GETGBL ;Branch if none MOV GR.ADR(R1),R1 ;R1 = addr/32 of region BR 40$ ;Go merge; save parameters .SBTTL GETGBL - Allocate Global Region (XM Only) ;+ ; Part of the installation of XM will allocate a global region ; ; The memory requirements were formerly fixed, but are now calculated ; dynamically via the MEMUMX symbol. ;- GETGBL: MOV @#$SYPTR,R4 ;R4 = base of RMON MOV $MEMPT(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 10$: CMP #-1,(R5)+ ;End of list ? BNE 10$ ;No, keep looking for it! ; R5 -> Start of region control block area 20$: CMP #-1,@R5 ;End of RCB list ? BEQ XD.BAD ;Yes, branch ; failure TST @R5 ;Empty entry ? BEQ 30$ ;Yes, branch; found an entry ADD #GR.ESZ,R5 ;Point to next entry BR 20$ ;Repeat ; R5 -> Entry RCB 30$: MOV P1$EXT(R4),R0 ;R0 -> extern routines MOV #MEMUMX,R2 ;R2 = size/32 memory MOV R3,-(SP) ;Save R3 CALL $XALPT(R0) ;Get memory from free list MOV (SP)+,R3 ;Restore R3 BCS XD.BAD ;Branch if not obtained! ; Build RCB MOV R2,(R5)+ ;Store the size/32 of region MOV R1,(R5)+ ;Store the addr/32 of region MOV #GR.PVT,(R5)+ ;Store status = Private MOV #UM$NAM,(R5)+ ;Store name 1st word (<^RDU >) MOV #<^R$>,@R5 ;Store name 2nd word .ENDC ;NE UM$DU FORTY$:: 40$: nop ; *** for debugging only *** remove when UM is perfect *** MOV R1,P1HIGH-UMBASE(R3) ;Store addr/32 of region in driver too MOV R1,XP1UMY-UMBASE(R3) ;store addr/32 UMY psect region, where ADD #/100,XP1UMY-UMBASE(R3) ;the BBR stuff is CLR R0 ;R0 = 0 ASHC #6,R0 ;R0,R1 contain physical addr MOV R0,XPHYSH-UMBASE(R3) ;Store physical high MOV R1,XPHYSL-UMBASE(R3) ;Store physical low MOV R0,XUMRL-UMBASE(R3) ;UBR(L/H) will hold the mapped version MOV R1,XUMRH-UMBASE(R3) ;of the physical start address of DU - ;they are initialized to the physical ;address MOV R0,-(SP) MOV R4,-(SP) MOV R3,-(SP) MOV SENTRY,R4 ;make R4 point to the $PNAME table SUB SSLOT,R4 ;entry for the handler MOV R0,R2 ;make R2 the high order bits of the ;physical address which we will pass ;to UB.ALL .IF NE UM$DU MOV #NUMRS,R0 ;we need NUMRS contiguous permanent ;UMR registers .ENDC ;NE UM$DU .IF NE UM$MU MOV #NUMRS-MU$FSM,R0 ;leave out the one for FSM .ENDC ;NE UM$MU MOV @#$SYPTR,R3 MOV $H2UB(R3),R3 CALL UB.ALL(R3) ;allocate the Unibus Mapping Registers MOV (SP)+,R3 ;*C* need to get back R3 BCS 60$ MOV R2,XUMRH-UMBASE(R3) ;*C* store physical low MOV R1,XUMRL-UMBASE(R3) ;*C* store physical high .IF NE UM$MU .IF NE MU$FSM MOV R2,-(SP) ;save previous result MOV R3,-(SP) ;save base again MOV #1,R0 ;Need 1 more UMR MOV R3,R4 ;get saved handler base address MOV R4,R1 ;put it here too ADD #,R4 ;Point to FSM identifier ADD #,R1 ;Point to HDR1LB CLR R2 ;High bits of HDR1LB MOV @#$SYPTR,R3 MOV $H2UB(R3),R3 CALL UB.ALL(R3) ;Allocate UMR for FSM MOV (SP)+,R3 ;*C* BCS 50$ ;branch out on failure MOV R1,(R3) ;save UNIBUS address of HDR1LB in FSM MOV R2,(R3) ADD #,R1 ADC R2 MOV R1,(R3) ;save UNIBUS address of LABLIO in FSM MOV R2,(R3) 50$: MOV (SP)+,R2 ;get Matthew's result .ENDC ;NE MU$FSM .ENDC ;NE UM$MU 60$: MOV (SP)+,R4 ;*C* MOV (SP)+,R0 ;*C* BCS XD.BAD MOV R2,R0 ADD #2,@SP ;Success: return to call+4 XD.BAD: RETURN ;Return to call+2 for errors .DSABL LSB .ENDC ;NE MMG$T .SBTTL RNGSET - initialize message ring pointers ;+ ;RNGSET - this routine initializes RNGSH,RNGSL and MRPTR to point ;to message ring 0. ;If we are doing MSCP error logging, it initializes each PC.MRG ;field in each port's entry in the Port Control table to point to the ;message ring for that particular port ; ; Called by ONCE ; ; Input: ; ; R3 -> Handler + 6 ; ;- RNGSET: MOV R0,-(SP) ;protect registers MOV R1,-(SP) MOV R2,-(SP) MOV R3,-(SP) ;first, make R1,R0 into a 22-bit pointer to the address of the first ;ring MRNG0. This is dependent upon whether or not we are in XM. ;R1 is the low order part. .IF EQ MMG$T CLR R0 ;only 16 bits in FB/SB MOV #MRNG0,R1 ;R1 -> offset from UMBASE to $RELF .-2 MRNG0 UM ;MRNG0 ADD R3,R1 ;add in (Handler + 6) .IFF ;EQ MMG$T MOV #MRNG0,R1 ;R1 -> MRNG0 (virtual) $RELF .-2 MRNG0 UMX ; SUB #BEGREL,R1 ;R1 = PAR1 offset MRNG0, add MOV R1,-(SP) ;this to start of high mem MOV XUMRL-UMBASE(R3),R1 ;region MOV XUMRH-UMBASE(R3),R0 ADD (SP)+,R1 ADC R0 .ENDC ;EQ MMG$T ;now stuff RNGSH,RNGSL with address. ;Also stuff MRPTR. MOV R1,XMRPTR-UMBASE(R3) MOV R1,XRNGSL-UMBASE(R3) MOV R0,XMRPTR-UMBASE+2(R3) MOV R0,XRNGSH-UMBASE(R3) .IF EQ UM$ERL-2 MOV #MBUFF0,R2 ;put the virtual (mapped) address of $RELF .-2 MBUFF0 UMX ;the mscp response buffers in BUFPTR MOV R2,XBUFPTR-UMBASE(R3) .ENDC ;EQ UM$ERL-2 ;if we are doing mscp error logging then we must initialize each ;PC.MNG field in each entry of the port control table with ;the address of the appropriate message ring. .IF EQ UM$ERL-2 ;first do port 0. MOV R1,XPCTAB-UMBASE+PC.MNG(R3) ;Store low 16-bits MOV R0,XPCTAB-UMBASE+PC.MNG+2(R3) ;Store high bits MOV R3,R2 ADD #XPCTAB-UMBASE+PC.MNG,R2 ;R2 points to the message ring ;address entry for port 0 in the port control ;table ;done with port 0, this loop does the remaining ports .IF NE UM$PORTS-1 MOV #UM$PORTS-1,R0 10$: MOV @R2,PC.ESZ(R2) ;Get to the next entry in the MOV 2(R2),PC.ESZ+2(R2) ;port control table by adding ADD #PC.ESZ,R2 ;PC.ESZ to the current pointer ADD #RINGLN,@R2 ;Get the next message ring .IF NE MMG$T ADC 2(R2) ;address by adding RINGLN to .IFF ;NE MMG$T ;the previous port CLR 2(R2) .ENDC ;NE MMG$T CLR 4(R2) ;INIT code will define the CLR 6(R2) ;virtual address. initialize DEC R0 ;tail pointer BGT 10$ .ENDC ;NE UM$PORTS .ENDC ;EQ UM$ERL-2 MOV (SP)+,R3 ;restore registers MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 RTS PC .IF NE UM$MU ;If magtape .IF NE MMG$T .ASSUME . LE LDBUF1+<1*BLK>,<;INSTALL overlay too big> . = LDBUF1+<1*BLK> ;Align UMXDAT Psect on block boundary .IFF ;NE MMG$T .PSECT SETOVR .ASSUME . LE OVRBK1+<2*BLK>,<;INSTALL overlay too big> .ENDC ;NE MMG$T .ENDC ;NE UM$MU .IF NE UM$DU .IF NE MMG$T .ASSUME . LE LDBUF1+<1*BLK>,<;INSTALL overlay too big> . = LDBUF1+<1*BLK> ;Align UMBOOT Psect on block boundary .ENDC ;NE MMG$T .ENDC ;NE UM$DU .SBTTL .DRBEG - Handler Entry Point .NLIST BEX .ENABLE LC,LSB ;+ ; Standard RT-11 entry point (BEGIN is logical entry point later in code). ;- .IF EQ MMG$T .DRBEG UM .IFF ;EQ MMG$T .DRBEG UM,SPFUN=SPFTAB,NSPFUN=NSPTAB .ENDC ;EQ MMG$T UMBASE =: UMSTRT + 6 ;Relocation off $ENTRY UM:: BR ACROSS ;Skip next location ;+ ; Global status storage location. (diagnostics usage) ;- STATU$::.WORD 0 ;status word accessed via the .DRTAB ;offset as the table named "UMS" ;BBFORC::.WORD 0 ;Debugging word .IF NE MMG$T .SBTTL - SPFTAB Set up .SPFUN table for UB handler ;SPFTAB - ;This is not "the real" special function table: this is the table of spfun's ;that the UB (Unibus Mapping) register hardware handler considers to be DU's ;normal (treat like read/write) special functions. Notice that the macro ;invocations do NOT include the TYPE= argument. .IF NE UM$DU SPFTAB: .DRSPF -, .DRSPF -, .WORD 0 NSPTAB: .DRSPF -, .DRSPF -, .WORD 0 .ENDC ;NE UM$DU .IF NE UM$MU SPFTAB: .DRSPF -, .DRSPF -, .DRSPF -, .WORD 0 NSPTAB: .DRSPF -, .WORD 0 .ENDC ;UM$MU .ENDC ;NE MMG$T ;+ ; Begin executing ;- ACROSS: MOV UMCQE,R5 ;R5 -> current Q-element .IF NE UM$MU ;If magtape .IF NE MMG$T ;If XM CALL CVPHYS ;Cnvrt buffer virtual addr to physical .ENDC ;NE MMG$T ;note: this destroys R3 .ENDC ;NE UM$MU LOW2HI BEGIN ;Go to start of handler .DSABL LSB .SBTTL Impure - Vector Table (if multi-port) and Impure Data Allocation .IF NE UM$PORTS-1 ;+ ; Vector table for multi-port configuration. ;- .DRVTB UM,UM$VEC,UMINT $1=1 .REPT UM$PORTS-1 .IRP Y,\$1 .DRVTB ,UM$VC'Y,U'Y'INT $1=$1+1 .ENDR .ENDR .Assume . LE ,<;SET object not in block1> .ENDC ;NE UM$PORTS-1 ;********************************************************************* .PSECT UMLORD ;********************************************************************* ;***********"Mirror Mirror on the wall who's data is it off"********** ;+ ; Impure area ***** THE FOLLOWING ITEMS MUST BE KEPT IN ORDER ***** ; An equivalent set of values will be defined in the UMX 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 these 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. ;- CLONE UDAIP,<:: .WORD UM$CSR> ;Address and polling register. CLONE UDASA,<:: .WORD UM$CSR+2> ; Status and Address register. CLONE ISTEP,<: .WORD 0> ;Next step bit, Done if less than 0. CLONE INISEQ,<: .WORD 0> ;Pts into INILST to next item to load CLONE INILST,<: .BYTE UM$VEC/4!IE> ;INIT 1 LO: vector and IE for init. ;STEP(=200) is the mandatory bit 15=1 ;for step 1 of the four step init .IF NE UM$ERL-2 CLONE ,< .BYTE 0*10+0+STEP> ;INIT 1 HI: 2^0 both Msg & Cmd rings. .IFF ;NE UM$ERL-2 CLONE ,< .BYTE 0*10+2+STEP> ;INIT 1 HI: 2^2 Msg, 2^0 Cmd rings. .ENDC ;NE UM$ERL-2 ;MRPTR is stuffed with a message ring address by RNGSET at ;load/fetch time - it will never change iff we have just one port CLONE MRPTR,<: .WORD 0> ;INIT 2: Message ring address 15:0 CLONE ,< .WORD 0> ;INIT 3: Message ring address 21:16 .IF NE UM$ERL-2 CLONE ,< .WORD GO> ;INIT 4: Go. .IFF ;NE UM$ERL-2 CLONE ,< .WORD DO.LFP!GO> ;INIT 4: Send an LFP, and then Go. .ENDC ;NE UM$ERL-2 CLONE TLPTR,<: .WORD> ;the tail pointer, actually an offset ;from the beginning of the ring ;+ ; UQSSP Communication area. It is highly recommended to read the UQSSP ; code before studying this code. ;- PTRLEN ==: 4 ;length in bytes of one UQSSP ;ring descriptor .IF NE UM$ERL-2 MQSIZE ==: 1 ;number of descriptors in .IFF ;NE UM$ERL-2 ;each response ring MQSIZE ==: 4 .ENDC ;NE UM$ERL-2 $1 = . CLONE INTS0, <: .BLKW 2> ;Interrupt identity area. $2 = . CLONE MRNG0, <:: .WORD 0,0> ;Message ring, 1 entry. .IF EQ UM$ERL-2 CLONE ,< .WORD 0,0> ;If doing MSCP error logging, CLONE ,< .WORD 0,0> ;then all response rings are CLONE ,< .WORD 0,0> ;four entries long .ENDC ;EQ UM$ERL-2 MRNGLN =: . - $2 $2 = . CLONE CRNG0, <:: .WORD 0,0> ;Command ring, 1 entry. CRNGLN = . - $2 CLONE VADR0, <: .WORD 0> CLONE TLP0, <: .WORD 0> ;tail pointer, an offset RINGLN =: . - $1 .IF EQ UM$ERL-2 ;do MSCP error logging .IF NE UM$PORTS - 1 CLONE INTS1, <: .BLKW 2> ;Interrupt identity area. CLONE MRNG1, <:: .WORD 0,0> ;Message ring, 4 entries. CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE CRNG1, <:: .WORD 0,0> ;Command ring, 1 entry. CLONE VADR1, <: .WORD 0> CLONE TLP1, <: .WORD 0> ;tail pointer, an offset .IF NE UM$PORTS - 2 CLONE INTS2, <: .BLKW 2> ;Interrupt identity area. CLONE MRNG2, <:: .WORD 0,0> ;Message ring, 4 entries CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE CRNG2, <:: .WORD 0,0> ;Command ring, 1 entry. CLONE VADR2, <: .WORD 0> CLONE TLP2, <: .WORD 0> ;tail pointer, an offset .IF NE UM$PORTS - 3 CLONE INTS3, <: .BLKW 2> ;Interrupt identity CLONE MRNG3, <:: .WORD 0,0> ;Message ring, 4 entries CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE ,< .WORD 0,0> CLONE CRNG3, <:: .WORD 0,0> ;Command ring, 1 entry. CLONE VADR3, <: .WORD 0> CLONE TLP3, <: .WORD 0> ;tail pointer, an offset .ENDC ;NE UM$PORTS-3 .ENDC ;NE UM$PORTS-2 .ENDC ;NE UM$PORTS .ENDC ;EQ UM$ERL-2 ;+ ; Miscellaneous data area ;- .IF NE UM$DU CLONE $HBT,<: .WORD 0> ;Test for blk 65,535? 0 = YES. ;.Assume . LE UMSTRT+BLK,<;SET object not in block1> .ENDC ;NE UM$DU .IF NE UM$ERL CLONE $SUCS,<: .WORD 0> ;Success log flag, 0 = YES. ;.Assume . LE UMSTRT+BLK,<;SET object not in block1> .ENDC ;NE UM$ERL CLONE $RETRY,<: .WORD NRETRY> ;Retry value (set code) ;.Assume . LE UMSTRT+BLK,<;SET object not in block1> .IF EQ UM$ERL-2 CLONE BUFPTR,<: .WORD 0> ;a virtual (mapped) address of the MSCP .ENDC ;EQ UM$ERL-2 ;response buffers ;+ ; In XM, physical memory address of the high memory portion of DU ; In FB,SB PHYSH will always be zero. ;- ; !!!!!!!!!!!!! CRITICALLY ORDERED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CLONE PHYSH,<: .WORD 0> ;Physical high 16-bits CLONE PHYSL,<: .WORD 0> ;Physical low ; end critical ordering ;+ ; the physical address of the start of the high memory portion of ; of the handler will be passed to the monitor's UB (the UMR hardware ; handler) routines and converted to a mapped UB address. the mapped ; UB address will be stored in UMR(H/L), and this mapped address is ; the address that should be given to controllers for DMA. If UB is ; not loaded, then this address will equal PHYS(H/L) ;- ; !!!!!!!!!!!!! CRITICALLY ORDERRED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CLONE UMRH,<: .WORD 0> ;UB mapped physical high 16-bits CLONE UMRL,<: .WORD 0> ;UB mapped physical low ; end critical ordering ;+ ; The 22-bit address of the first response ring ; In FB,SB RNGSH will always be zero. ;- CLONE RNGSH,<: .WORD 0> ;Physical high 16-bits CLONE RNGSL,<: .WORD 0> ;Physical low ;+ ; The 22-bit address of the first response buffer ; In FB,SB BUFSH will always be zero. ;- CLONE BUFSH,<: .WORD 0> ;Physical high 16-bits CLONE BUFSL,<: .WORD 0> ;Physical low ;+ ; this is where we store the PAR1 bias of the UMY psect ; it is initialized by the ONCEXM routine ;- CLONE P1UMY,<: .WORD 0> ;+ ; UTTAB - Unit Translation Table. ; ; For DU: ; Initially, the RT-11 unit numbers translate 1 to 1 with ; the MSCP unit numbers. ; ; RT-11 unit 0 is unit 0 of port 0; ; RT-11 unit 1 is unit 1 of port 0; ; RT-11 unit 2 is unit 2 of port 0; etc... ; ; For MU: ; Initially, the RT-11 unit numbers translate 1 to 1 with ; the port numbers (only one tape drive per controller). ; ; RT-11 unit 0 is unit 0 of port 0; ; RT-11 unit 1 is unit 0 of port 1; ; RT-11 unit 2 is unit 0 of port 2; ; RT-11 unit 3 is unit 0 of port 3. ; ; The values may be SET. ;- CLONE .UTTAB,<::.WORD 0> ;Pointer to current UTTAB entry. ;Beginning of table for SPFUN CLONE UMTAB,<:: .WORD UM$NAM> ;Table ID (Rad50 'MU' or 'DU') CLONE ,< .WORD UM$UNITS> ;Table for "n" units CLONE UTTAB,<:: .ASSUME UT.UNIT EQ 0> ;MSCP unit number (word) .IF NE UM$DU .ASSUME UT.PART EQ 2 ;Partition number (byte, Disk only) .ENDC ;NE UM$DU .IF NE UM$MU .ASSUME UT.JOB EQ 2 ;Job # byte (byte, Tape only) .ENDC ;NE UM$MU .ASSUME UT.PORT EQ 3 ;UDA Port to use: <0 => invalid (byte) $1=0 .REPT UM$UNITS .IRP Y,\$1 CLONE ,< .WORD UM$U'Y> ;unit number CLONE ,< .BYTE UM$A'Y> ;partition number CLONE ,< .BYTE UM$O'Y> ;port number $1=$1+1 .ENDR .ENDR CLONE UTTBND,< ==: .> ;Table end ;+ ; PCTAB - Port control table. ; ; For DU: ; Initially, all port entries are set to 0. May be SET to other values. ; For MU: ; Port entries correspond to RT-11 unit numbers, since there's only one ; tape per controller. For drive w/settable TMSCP unit number, set # =0. ;- .ASSUME PC.AIP EQ 0 ;Initialization and Polling register .ASSUME PC.ASA EQ 2 ;Status register .ASSUME PC.STEP EQ 4 ;Initialization step for this port .ASSUME PC.VEC EQ 6 ;Vector for this port .ASSUME PC.NUM EQ 10 ;the port's number (0..UM$PORTS-1) .IF EQ UM$ERL-2 .ASSUME PC.MNG EQ 12 ;message ring address for this port .ASSUME PC.ADR EQ 16 ;pointer to virtual addr. of the ring .ASSUME PC.BUF EQ 20 ;tail pointer for the ring. .ASSUME PC.ID EQ 22 ;controller's software/hardware ;version numbers, and identifier .ASSUME PC.VOL EQ 34 ;volume's (pack's) identifier .ENDC ;EQ UM$ERL-2 .Assume UTTBND EQ PCTAB,<;UT table and PC table not contiguous> $1 = . CLONE PCTAB,<:: .WORD UM$CSR,UM$CSR+2>;Port 0 (multi-port only) CLONE ,< .WORD 0,UM$VEC/4!IE> ; CLONE ,< .WORD 0> ;store the port's number .IF EQ UM$ERL-2 CLONE ,< .WORD 0,0> ;message ring address CLONE ,< .WORD 0> ;message ring tail pointer (an offset) CLONE ,< .WORD 0> ;16 bit pointer to ring's buffers CLONE ,< .BLKW 5> ;controller ID and C(h/s)vrsn CLONE ,< .BLKW 2> ;volume serial number .ENDC ;EQ UM$ERL-2 ; PC.ESZ is the size of a port control table entry ;NOTE: This inline calculation of the size of a port control table entry ; is, because of the use of the CLONE macro, is sensitive to monkeying ; with the PSECT's. PC.ESZ =: .-$1 ; PC.ESZ depends upon error logging .IF EQ UM$ERL-2 .ASSUME PC.ESZ EQ 40 .IFF ;EQ UM$ERL-2 .ASSUME PC.ESZ EQ 12 .ENDC ;EQ UM$ERL-2 .IF NE UM$PORTS-1 $1=1 .REPT UM$PORTS-1 .IRP Y,\$1 CLONE ,< .WORD UM$CS'Y,UM$CS'Y+2> ;Port Y data. CLONE ,< .WORD 0,UM$VC'Y/4!IE> ; CLONE ,< .WORD 'Y> ;store the port's number .IF EQ UM$ERL-2 CLONE ,< .WORD 0,0> ;message ring address CLONE ,< .WORD 0> ;message ring tail pointer (an offset) CLONE ,< .WORD 0> ;16 bit pointer to ring's buffers CLONE ,< .BLKW 5> ;controller ID and C(h/s)vrsn CLONE ,< .BLKW 2> ;volume serial number .ENDC ;EQ UM$ERL-2 $1=$1+1 .ENDR .ENDR .ENDC ;NE UM$PORTS-1 CLONE .PCTAB,<::.WORD 0> ;Pointer to current PCTAB entry. .IF NE UM$DU ;+ ; PSTAB - Partition Size Table (DU only) ; ; PSTAB is an array of RT-11 disk device unit partition sizes in blocks. ; The DU specific I/O routine (IOXFER in DU.MAC) checks sixteen bit ; LBNs requested against any value entered in PSTAB. An LBN that ; would exceed the MSCP unit size is rejected. ; ; PSTAB is kept valid by updating it with the MSCP unit size ; returned in the End Message for the Online Command. ; A zero entry in PSTAB indicates that the MSCP unit on which the ; RT-11 disk device unit is defined (via UTTAB above) has not yet ; been brought Online. ; CLONE .PSTAB,<::.WORD 0> CLONE PSTAB,<::.WORD 0> .IF GT .REPT CLONE ,< .WORD 0> .ENDR .ENDC ;GT .ENDC ;NE UM$DU ;************************ "End of Reflection" ************************ ;********************************************************************* .PSECT UMDATA ;********************************************************************* ;+ ; Command/Response buffer area !!!!Critically Ordered!!!! ;- ;the next two words are the command message envelope LN.CMD::.WORD P.CSIZ ;Length of command. VC.CMD::.WORD VIR.ID ;Command virtual circuit ID. CBUFF0::.BLKB P.CSIZ ;Command buffer: 1 full packet ;the next two words are the response message envelope $1 = . LN.RSP::.WORD P.MSIZ ;Length of response. VC.RSP::.WORD VIR.ID ;Response virtual circuit ID. MBUFF0::.BLKB P.MSIZ ;Message buffer: 1 full packet .IF EQ UM$ERL-2 .REPT 3 ;3 datagrams per MSCP command .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit .BLKB P.MSIZ ;Message buffer: 1 full packet .ENDR .ENDC ;EQ UM$ERL-2 BUFSSZ =: .-$1 .IF EQ UM$ERL-2 .IF NE UM$PORTS - 1 ;the next two words are a response message envelope .WORD P.MSIZ .WORD VIR.ID ;Response virtual circuit ID. MBUFF1::.BLKB P.MSIZ ;Message buffer: 1 full packet .REPT 3 ;3 datagrams per MSCP command .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit ID. .BLKB P.MSIZ ;Message buffer: 1 full packet .ENDR .IF NE UM$PORTS - 2 ;the next two words are a response message envelope .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit ID. MBUFF2::.BLKB P.MSIZ ;Message buffer: 1 full packet .REPT 3 ;3 datagrams per MSCP command .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit ID. .BLKB P.MSIZ ;Message buffer: 1 full packet .ENDR .IF NE UM$PORTS - 3 ;the next two words are a response message envelope .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit ID. MBUFF3::.BLKB P.MSIZ ;Message buffer: 1 full packet .REPT 3 ;3 datagrams per MSCP command .WORD P.MSIZ ;Length of response. .WORD VIR.ID ;Response virtual circuit ID. .BLKB P.MSIZ ;Message buffer: 1 full packet .ENDR .ENDC ;NE UM$PORTS - 3 .ENDC ;NE UM$PORTS - 2 .ENDC ;NE UM$PORTS - 1 .ENDC ;EQ UM$ERL-2 ;+ ; Miscellaneous data area ;- .IF NE MMG$T ;********************************************************************* .PSECT UMLDATA ;********************************************************************* P1HIGH::.WORD 0 ;UMX mapping P1EXT$: .WORD 0 ;Store P1EXT ptr here .ENDC ;NE MMG$T $1=0 .REPT UM$PORTS ;allocate a fork block for each port .IRP Y,\$1 $2 = . UMFBL'Y: .WORD 0,0,0,0 FKBKLN = . - $2 .ENDR $1=$1+1 .ENDR .IF EQ UM$ERL-2 QIO.NL: ;if we are doing MSCP error logging, .REPT Q.ELGH ;then we need an "empty" queue element to .WORD 0 ;pass to the error logger with datagrams .ENDR ;in the case that we do not have the real ;queue element for the command that actually .ENDC ;EQ UM$ERL-2 ;provoked the datagram .IF NE UM$ERL .WORD P.MSIZ ;Define ERLBUF with UQSSP envelope .BLKW 1 ;and sized to maximum. ERLBUF::.BLKB P.MSIZ ;Error log packet buffer: LOW memory .ENDC ;NE UM$ERL ;********************************************************************* .PSECT UMDATA ;********************************************************************* RETRY: .WORD 0 ;Retry counter. NEXT: .WORD 0 ;Address of post interrupt activity. .IF NE DU$BBR AFTBBR: .WORD 0 ;place to save NEXT while we go off .ENDC ;NE DU$BBR ;to BBR land INTEOP: .WORD 0 ;Internal operation in progress! CRFNUM: .WORD 0 ;command reference number counter SAREG: .WORD 0 ;place to hold contents of SAR ;MUST only be accessed after .FORK!!! FUNTYP: .BYTE ;copy of Q$FUNC byte in queue element .EVEN .IF EQ UM$ERL-2 ;if we are doing mscp error logging, QCHECK: .BLKW 2 ;then we keep a copy of the queue .ENDC ;EQ UM$ERL-2 ;element address - it is kept until ;we are about to .DRFIN. ;When a datagram arrives, then if ;QCHECK matches the queue element ;address stored in the packet, then ;we know we have the queue element for ;the I/O request .IF NE MMG$T P1LOW:: .WORD 0 ;Store new mapping low here (UMR) ; Relocate some pointer locations .IF NE UM$ERL H$ELPT: .WORD 0 ;Error logging entry .ENDC ;NE UM$ERL 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 memory ptr H$CQE: .WORD 0 ;UMCQE high memory ptr .ENDC ;NE MMG$T ;********************************************************************* .PSECT UMLCODE ;(To setup UMXPSECT Macro) ;********************************************************************* .IF NE MMG$T .SBTTL HCALL - Handle Low Memory to High Memory Control Transfers ;+ ; HCALL - Handle Low Memory to High Memory Control Transfers ; ; Called via the following: ; ; MOV #DESTIN,-(SP) ;Destination (in high memory region) ; $REL .-2 DESTIN UMX ; ... ; MOV @#KISAR1,-(SP) ;Store old mapping ; .P1EXT P1HIGH ; (Map to high memory) ; CALLR @#HCALL ;To common control transfer point ; $REL .-2 HCALL UMX ; ... ; .P1END ; (End of block) ;- ;********************************************************************* UMXPSECT ;********************************************************************* HCALL:: MOV 2(SP),SP ;Restore stack from P1EXT arguments CMP (SP)+,(SP)+ ;Pop return arguments MOV (SP)+,R0 ;Restore R0 MOV (SP)+,P1LOW ;Store low mapping RETURN ;Transfer to destination .ENDC ;NE MMG$T .SBTTL BEGIN - Handler Start: Executable Code Begins ;+ ; The RT-11 unit in the queue element is used to find the corresponding ; MSCP unit and port. The data structures which belong to this unit are ; loaded into the 'active' set, (data structures = port values, and for ; DU, bbr values). Some flags are initialized and, if the function is ; is to get the translation table we jump to the corresponding routine ; immediately. ;- .ENABL LSB BEGIN:: MOV $RETRY,RETRY ;Reset error threshold. BEGIN1:: CLR INTEOP ;Indicate no internal operation ;in progress. .IF NE UM$DU ;If disk, make R5 -> queue element .IF NE MMG$T MOV #UMCQE,R5 ;the current queue element pointer $REL .-2 UMCQE UMR ;set up by .DRBEG is located in .HP1EX P1LOW ;low memory MOV @R5,R5 .HP1EN MOV R5,H$CQE ;Store Q-element ptr here .IFF ;NE MMG$T MOV UMCQE,R5 ;(in case BBR code after ON_LINE ; got us here) .ENDC ;NE MMG$T .ENDC ;NE UM$DU .IF NE UM$MU ;If magtape CLR ABTFLG ;Initialize abort flag .ENDC ;NE UM$MU .IF NE UM$DU .IF NE DU$BBR CLR BBRON ;Clear BBR in progress flag .ENDC ;NE DU$BBR .ENDC ;NE UM$DU MOVB Q$FUNC(R5),FUNTYP .IF NE UM$N64 MOV @#$SYPTR,R0 BIT #CF3.64,$CNFG3(R0) BEQ 10$ ;if the function byte is for an spfun, ;the following BIS will produce ;the value of the SPFUN code, BISB #U64MSK,FUNTYP ;making type look like a spfun code 10$: .ENDC ;NE UM$N64 .IF NE UM$MU CMPB FUNTYP,#SF.MTB ;Is this an MU table .SPFUN ? .IFF ;NE UM$MU CMPB FUNTYP,#SF.TAB ;Is this a DU table .SPFUN ? .ENDC ;NE UM$MU BNE 20$ ;No, then go set up the port. CALLR DOTAB ;Yes, then go read or write table 20$: .IF NE UM$MU ;If Magtape CALLR MUBEGN ;Do magtape specific startup BEGN01: MOV R3,R2 ;(Magtape rejoins us here) .ENDC ;NE UM$MU ;GET THE UNIT NUMBER - MAKE A POINTER INTO THE UNIT TRANSLATION TABLE .IF NE UM$DU ;If disk MOVB Q$UNIT(R5),R2 ;Get the unit number. BIC #^C,R2 ;Isolate the unit number. .IF NE UM$N64 MOV @#$SYPTR,R0 ;if we are in a 64-unit handler do BIT #CF3.64,$CNFG3(R0) ;the following BEQ 40$ MOV R2,-(SP) ;save low three bits of unit number MOVB Q$FUNC(R5),R2 ;get function byte from queue element BPL 30$ ;if high bit is set, COM the value COM R2 30$: BIC #^C,R2 ;zero low four bits, and put the high ASR R2 ;three bits of unit number in correct BIS (SP)+,R2 ;place. Put in low three bits CMP R2,#UM$UNITS ;although monitor has checked the unit BHIS HARDEX ;number, in a 64-unit handler it's ;possible to have unit numbers less ;than 64; in fact the default value ;of DU$UNITS in 64-unit handler is 16. ;Thus, for a 64-unit handler, we must ;compare the unit number with UM$UNITS 40$: .ENDC ;NE UM$N64 .ENDC ;NE UM$DU ASL R2 ;Transform RT-11 device unit number ;to index into DU/MU data structures. .IF NE UM$DU MOV #PSTAB,.PSTAB $REL .-4 PSTAB UMX ADD R2,.PSTAB .ENDC ;NE UM$DU ASL R2 .ASSUME UT.ESZ EQ 4 ADD #UTTAB,R2 ;R2 -> unit translation $REL .-2 UTTAB UMX ;$REL knows if we are in XM or not MOV R2,.UTTAB ;Save R2 for later. ;GET A POINTER TO THE PORT CONTROL TABLE ENTRY FOR THE PORT ;THAT GOES WITH THIS RT11 UNIT ;if we are supporting more than one port, we must copy this port's ;(the port whose I/O request we are starting) parameters into the ;impure area, so that it can initialized properly, so that we are ;talking through the correct CSR as so forth. .IF EQ UM$PORTS-1 MOV #PCTAB,R3 ;if there is only one port, then $REL .-2 PCTAB UMX ;#PCTAB is the one entry MOV R3,.PCTAB ;if there is only one port, then the table beginning at UDAIP ;never needs to be changed .IFF ;EQ UM$PORTS-1 MOVB UT.PORT(R2),R3 ;R3 = Port number. BMI HARDEX ;Branch if invalid port; exit ASL R3 ;Create index into the port control ; table by shifting left. MOV R3,R1 ;Save (*2) in R1 for two places below ASL R3 ; *4 ASL R3 ; *8 .IF NE UM$ERL-2 .ASSUME PC.ESZ EQ 12 ADD R1,R3 ;add in (*2) NOTE:R1 will be used ;again below for ACTBBR calculation .IFF ;NE UM$ERL-2 .ASSUME PC.ESZ EQ 40 ASL R3 ; *16. ASL R3 ; *32. .ENDC ;NE UM$ERL-2 ADD #PCTAB,R3 ;R3 -> port control table entry $REL .-2 PCTAB UMX ;$REL knows if we are in XM or not MOV #UDAIP,R4 ;R4 -> impure table $REL .-2 UDAIP UMX ;$REL knows if we are in XM or not MOV R3,.PCTAB ;Save pointer for later. MOV (R3)+,(R4)+ ;Get the CSR, it is the IAP. .ASSUME PC.AIP EQ 0 MOV (R3)+,(R4)+ ;Get the ASA. .ASSUME PC.ASA EQ 2 MOV (R3)+,(R4)+ ;Get the new ports init status. .ASSUME PC.STEP EQ 4 MOVB (R3),INILST ;Get the new ports vector code. .ASSUME PC.VEC EQ 6 .IF EQ UM$ERL-2 ;the following entries in the port control table were defined ;by the load-time routine RNGSET ADD #4,R3 .ASSUME PC.MNG EQ 12 MOV (R3)+,MRPTR ;PC.MNG MOV (R3)+,MRPTR+2 .ENDC ;EQ UM$ERL-2 MOV .PCTAB,R3 .ENDC ;NE UM$PORTS-1 ;IF WE ARE DOING DU BAD BLOCK REPLACEMENT, THEN WE MAKE POINTER TO THE ;APPROPRIATE BBRTAB ENTRY .IF NE UM$DU .IF NE DU$BBR .IF NE UM$PORTS-1 ASL R1 ;R1 = port*4 .IF EQ UM$ERL-2 .ASSUME BB.SIZ EQ 30 ASL R1 ; *8 MOV R1,-(SP) ;Save R1 (= active port * 8) on stack ASL R1 ; *16. ADD (SP)+,R1 ;+ (port*8) = (port*24.) = offset to ;BBRTAB entry .IFF ;EQ UM$ERL-2 .ASSUME BB.SIZ EQ 22 ASR R1 ;divide by 2. MOV R1,-(SP) ;Save R1 (= active port * 2) on stack ASL R1 ;*8 ASL R1 ;*16. ADD (SP)+,R1 ;+ (port * 2) = port * 16. .ENDC ;EQ UM$ERL-2 .IFF ;NE UM$PORTS-1 CLR R1 ;R1 = only port zero .ENDC ;NE UM$PORTS-1 MOV R1,-(SP) MOV R2,-(SP) ADD #BBRTAB,R1 ;R1 -> BBR table entry $REL .-2 BBRTAB UMY ;it is in the UMY psect MOV #ACTBBR,R2 ;ACTBBR, in the UMY psect, is loaded $REL .-2 ACTBBR UMY ;with a pointer to the current BBRTAB .HP1EX P1UMY ;entry MOV R1,@R2 ;Store the current entry .HP1EN MOV (SP)+,R2 MOV (SP)+,R1 .ENDC ;NE DU$BBR .ENDC ;NE UM$DU .IF NE UM$MU ;If Magtape CMPB Q$FUNC(R5),#PURG.. ;Purge? BEQ 50$ ; do CLOSE code if so CMPB Q$FUNC(R5),#CLOS.. ;Close? BNE START ;No, go check if port is initialized 50$: MOV .UTTAB,R3 CALLR KLOSE ;Yes, close and go away. .ENDC ;NE UM$MU .BR START .DSABL LSB .SBTTL START - 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: TST ISTEP ;Is this port fully initialized? BPL INIT ;No, then go init it. MOV R4,-(SP) ;protect R4 MOV @UDASA,R4 ;get contents of SAR BIT #,R4 ;Has port errored or been reset? BEQ 10$ ;branch if not .IF NE UM$ERL CALL CNTERR ;Log SAR Packet. .ENDC ;NE UM$ERL MOV (SP)+,R4 ;restore R4 BR INIT 10$: MOV (SP)+,R4 ;restore R4 CALLR 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 20$ ;No, then do an initialization. HARDEX: CALLR UMHERR ;Yes, take the hard error exit. 20$: CLR @PC.AIP(R3) ;Strobe the UDA to start the init. MOV #ISTEP1,PC.STEP(R3) MOV #ISTEP,R4 ;R4 -> initialization parameters. $REL .-2 ISTEP UMX ; ... MOV #ISTEP1,(R4)+ ;Set step bit for step 1. MOV R4,@R4 ;Point INISEQ at INILST-2. ;INILST is an array of parameters ;that will each be passed to the ;controller during the initialization ;process INISTP: MOV #UDASA,R4 ;Get the I/O page addr of ;the status address (SA) register $REL .-2 UDASA UMX ;($REL knows if are in XM or not) MOV @(R4)+,R5 ;Get the contents of SA register BMI INIT ;ER (Bit 15) = 1 means port error, ;so try again BIT R5,@R4 ;R4 points to ISTEP which looks like ;an SA register with the appropriate ;step bit on. ;If the desired step bit is not on, BEQ INISTP ;wait for it; this is a "busy loop" MOV R4,R5 ;R5 => ISTEP ASL (R4)+ ;ASL makes ISTEP look like next ;expected step bit. ADD #2,(R4)+ ;increment INISEQ so that it points ;to the parameters to pass (via the SA ;register) to the to controller for ;next step MOV @-(R4),@-(R5) ;Send the parameter that INISEQ ;points to out to the controller via ;SA register. TST -(R4) ;Which step is this? .ASSUME ISTEP4*2 EQ 100000 BPL HRET ;Not step 4 so go RTS to wait for int. ;Wait for an interrupt, back at UMINT ;the .DRAST code will cause a jump ;back to INISTP: ;here if port is initialized, MOV .PCTAB,R5 ;so get address of pc table entry. MOV @R4,PC.STEP(R5) ;Save init state in port impure area. .BR RNGDEF ;Go initialize the rings' descriptors .SBTTL - Initialize the UQSSP rings of an initialized port RNGDEF: MOV .UTTAB,R0 ;what port is being initialized? MOVB UT.PORT(R0),R0 ;now we must initialize the buffer descriptors in the ring for ;the port we are initializing. note that R0 is currently the ;port number .IF EQ MMG$T MOV #MRNG0,R4 ;make R4 point to the port's $REL .-2 MRNG0 UM ;message ring MOV #MBUFF0,R1 ;make R1 point to the ring's $REL .-2 MBUFF0 UM ;buffers .IF EQ UM$ERL-2 30$: DEC R0 ;use the port number (=R0) to advance BMI 40$ ;the R4 and R1 to the port's ring ADD #RINGLN,R4 ;and buffer ADD #BUFSSZ,R1 BR 30$ ;at this point, R4 points to the message ring for this port, and ;R1 points to the appropriate set of message buffers 40$: MOV .PCTAB,R0 ;save the address of the set of buffers for MOV R1,PC.BUF(R0) ;this ring in the port control table MOV R4,PC.ADR(R0) ;also save the address of the address of ADD #MRNGLN+CRNGLN,PC.ADR(R0) ;start of the ring .ENDC ;EQ UM$ERL-2 MOV #CBUFF0,R0 $REL .-2 CBUFF0 UM ;the next two instructions initialize the command ring descriptor MOV R0,MRNGLN(R4) ;command ring is offset from CLR MRNGLN+2(R4) ;message ring by MRNGLN bytes MOV R1,(R4)+ ;put message buffer address in the message MOV #,(R4)+ ;ring descriptor - high order of buffer ;addr. is 0 in non-XM ;if we are doing MSCP error logging, then there are three more ring ;descriptors and three more buffers .IF EQ UM$ERL-2 MOV #3,R0 50$: ADD #P.MSIZ+ENVLEN,R1 MOV R1,(R4)+ ;put buffer addr. in ring descriptor MOV #,(R4)+ ;high order of addr. is 0 in non-XM DEC R0 BNE 50$ .ENDC ;EQ UM$ERL-2 .IFF ;EQ MMG$T MOV R3,-(SP) ;protect R3-> port control table entry MOV R0,R2 ;put the port number in R2 MOV UMRL,R0 ;R0 = global region phy base low MOV UMRH,R1 ;R1 = global region phy base high ADD #MBUFF0-UMXBASE,R0 ;Add offset to response buff ADC R1 ;Add carry to high part MOV R1,-(SP) ;save physical address of MBUFF0 MOV R0,-(SP) ;for use below when calculating ;address of CBUFF0 MOV #MBUFF0,R3 $REL .-2 MBUFF0 UMX MOV #MRNG0,R4 $REL .-2 MRNG0 UMX .IF EQ UM$ERL-2 ;The following loop will put ;the virtual (PAR1 offset) address 60$: DEC R2 ;of the appropriate ring into R4; BMI 70$ ;and the virtual (PAR1 offset) ADD #BUFSSZ,R3 ;of the ring's buffers into R3 ADD #BUFSSZ,R0 ADC R1 ADD #RINGLN,R4 BR 60$ 70$: ;at this point, R4 points to the message ring for this port, and ;R0-R1 points to the appropriate set of message buffers. Note that ;R4 is virtual (mapped) address, but R0-R1 are a physical address. MOV .PCTAB,R2 ;save virtual address of the ring's MOV R3,PC.BUF(R2) ;buffers in the port control table MOV R4,PC.ADR(R2) ADD #MRNGLN+CRNGLN,PC.ADR(R2) .ENDC ;EQ UM$ERL-2 ;the next four instructions initialize the command ring descriptor MOV (SP)+,MRNGLN(R4) ;command ring is offset from the ;message ring by MRNGLN bytes - the MOV (SP)+,MRNGLN+2(R4) ;buffer immediately precedes the ;first message buffer, MBUFF0 SUB #P.CSIZ+ENVLEN,MRNGLN(R4) SBC MRNGLN+2(R4) ;now we initialize the message ring descriptor MOV R0,(R4)+ ;put buffer address in the ring descriptor MOV R1,@R4 ;first low order, then high order BIS #,(R4)+ ;if we are doing MSCP error logging, then there are three more ring ;descriptors and three more buffers .IF EQ UM$ERL-2 MOV #MQSIZE-1,R2 80$: ADD #P.MSIZ+ENVLEN,R0 ADC R1 MOV R0,(R4)+ ;put buffer addr. in ring descriptor MOV R1,@R4 BIS #,(R4)+ DEC R2 BNE 80$ .ENDC ;EQ UM$ERL-2 MOV (SP)+,R3 ;restore R3 .ENDC ;NE MMG$T ;now initialize the tail pointer - which immediately follows the ;ring descripors ;at this point, R4 -> command ring for the port we are initializing ADD #CRNGLN,R4 MOV R4,@R4 ;Initialize the pointer to the first entry SUB #MRNGLN+CRNGLN,(R4) ;of the ring, this pointer, VADR, ;is a virtual address. CLR 2(R4) ;initialize the tail pointer which ;is actually an offset from VADR ;now the rings are all set up, so go commence the I/O operation CALLR START .SBTTL HRET - return from hi memory PAR1 space ; ;HRET - an invocation of RET2LO can cause a jump to HRET. Hret uses P1EXT ;to do a jump to a low memory PAR1 space (XM only). If this not XM, we ;simply return ; HRET: .IF NE MMG$T .HP1EX P1LOW ; (Map to low memory) CALLR @#LRET ;Return to low memory exit $REL .-2 LRET UMR ; ... .HP1EN ; (End of block) ;********************************************************************* UMPSECT ;********************************************************************* LRET: MOV 2(SP),SP ;Restore stack CMP (SP)+,(SP)+ ;Pop some arguments MOV (SP)+,R0 ;Restore R0 (in case come from abort) .ENDC ;NE MMG$T RETURN .DSABL LSB .SBTTL DISPAT - Function Dispatch Area ;+ ; DISPAT ; ; The Controller Characteristics are Set if we just completed a four step ; initialize and, if the function is not MSCP Bypass, the required unit is ; brought Online. ; ; For DISK, in the case of BBR, the Online is followed by a Get Status to ; obtain parameters needed in the BBR algorithm. If the command is MSCP ; BYPASS, the unit is NOT brought Online. This is to allow formatting of ; RQDX3 (?) disks, which must be Offline in order to be formatted. ; ; For TAPE, the Online is followed by a check if the unit supports caching, ; in which case we do a Set Unit Characteristics to enable the caching. ; ; All the above operations use 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 appropriate ; routine. ;- .ENABL LSB .IF NE MMG$T ;********************************************************************* UMXPSECT ;********************************************************************* .ENDC ;NE MMG$T DISPAT: .IF NE UM$MU ;If magtape CLR INTEXP ;Set no interrupts expected .ENDC ;NE UM$MU ASLB (PC)+ ;Do we have to set timeout value? INITFL: .WORD 0 ; (Init flag: timeout & unit online) .ASSUME IN.TMO EQ 200 BCC 20$ ;Branch if not, it's already set DO SETCC,THEN,10$ ;Set the timeout, then check ONLINE 10$: ;NOTE: the SCC command is done as an internal operation WITH ;recovery so that if we are here, the SCC succeeded .IF EQ UM$ERL-2 MOV P.CSVR(R5),PC.ID+10(R3) ;save the controller soft/hard version MOV P.CNTI(R5),PC.ID(R3) ;numbers in the port control table MOV P.CNTI+2(R5),PC.ID+2(R3);save the controller id., low order MOV P.CNTI+4(R5),PC.ID+4(R3);and then high order MOV P.CNTI+6(R5),PC.ID+6(R3) .ENDC ;EQ UM$ERL-2 ;see if we are handling a BYPASS function 20$: .IF NE UM$DU CMPB FUNTYP,#SF.OBY ;Is it the obsolete DU BYPASS code? BEQ 30$ ;Branch if yes .ENDC ;NE UM$DU CMPB FUNTYP,#SF.BYP ;Is this the BYPASS .SPFUN? BNE 50$ ;Branch if not handling a BYPASS I/O GETCQE -(SP) ;set up check for recovery option .IF NE UM$DU BR 40$ ;branch because we do not need to ;handle the obsolete BYPASS code .ENDC ;NE UM$DU .IF NE UM$MU .BR 40$ ;branch because we do not need to ;handle the obsolete BYPASS code .ENDC ;NE UM$MU .IF NE UM$DU 30$: GETCQE -(SP) ;set up check for recovery option ADD #,(SP) ;advance to Q$BLKN to Q$FUNC MOVB #SF.BYP,@(SP) ;replace the obsolete BYPASS code MOVB @(SP),FUNTYP ; with the correct BYPASS code SUB #,(SP) ;go back from Q$FUNC to Q$BLKN .ENDC ;NE UM$DU 40$: CMP #1,@(SP)+ ;Does user want Bypass recovery? .ASSUME Q$BLKN EQ 0 BNE 80$ ;Branch if not 50$: ASL INITFL ;Do we need to put unit on-line? .ASSUME IN.ONL EQ 100000 BCC 80$ ;Branch if not BRINON: ;here to bring unit online .IF NE UM$DU .IF EQ DU$BBR DO TRYONL,THEN,70$ ;Go do an internal online .IFF ;EQ DU$BBR DO TRYONL,THEN,60$ ;If host BBR, do an ONLINE command 60$: MOV #ACTBBR,R1 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV @R1,R1 MOV R5,BB.BUF(R1) ;save pointer to end message buffer MOV R4,BB.RNG(R1) ;save pointer to controller's ring MOV R3,BB.POR(R1) ;save pointer into port control table .HP1EN MOV P.STS(R5),R1 ;get status of the ONLINE command BIC #^c,R1 ;remove subcode BNE 70$ ;branch iff online failed .IF EQ UM$ERL-2 MOV P.VSER(R5),PC.VOL(R3) ;save the volume serial number MOV P.VSER+2(R5),PC.VOL+2(R3) MOV R2,-(SP) ;save the crf. and status of the MOV R3,-(SP) ;online command MOV R4,-(SP) MOV P.CRF(R5),R3 MOV P.CRF+2(R5),R4 MOV #ACTBBR,R2 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV @R2,R2 ;R2 = current BBRTAB entry MOV R3,BB.CRF(R2) ;save command reference number, low MOV R4,BB.CRF+2(R2) ;save command reference number, high MOV R1,BB.COD(R2) ;save the status/event code .HP1EN MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 .ENDC ;UM$ERL-2 JMP GETUS ;go check RCT (includes a GETNEXT) .ENDC ;EQ DU$BBR 70$: CALL GETNEXT ;flush the ring JMP BEGIN1 ;Retry the I/O operation under the ;error threshold regardless of ;success or failure of PUT UNIT ;ONLINE command. .ENDC ;NE UM$DU .IF NE UM$MU ;If Magtape DO ONLINE,THEN,70$ ;Do internal online, ;then check caching, 70$: CALL GETNEXT ; and settable densities CALLR CASHCK ;this will lead us back to DISPAT .ENDC ;NE UM$MU 80$: .IF NE UM$MU ;If Magtape TSTB @.DNTAB ;Should we set density? .ASSUME DN.DEN EQ 0 .ASSUME DN.SET EQ 200 BMI BRINON ;If MI, do it and return to DISPAT .ENDC ;NE UM$MU GETCQE R1 ;R1 -> current queue element .IF NE UM$MU ;If Magtape .IF EQ MMG$T ;If not XM MOV Q$BUFF(R1),OLDBA ;Save buffer address .ENDC ;EQ MMG$T CLR NFSREAD ;Clear NFS read flag .ENDC ;NE UM$MU MOVB Q$FUNC(R1),R4 ;R4 = function. Is it a plain-old, ;regular RT11 read/write/seek? .IF NE UM$N64 MOV @#$SYPTR,R0 ;Are we in a 64-unit monitor? BIT #CF3.64,$CNFG3(R0) ;branch if yes BEQ 90$ BIC #U64MSK,R4 ;remove high 3 bits of unit number ;we BIC, so that R4 becomes the Q$FUNC ;value of a read/write/seek. For ;.SPFUN codes we needed to BIS - ;that value is in FUNTYP 90$: TST R4 ;RESTORE CONDITION FLAGS! .ENDC ;NE UM$N64 BEQ 140$ ;Branch if yes, it is a "normal" ;RT11 read/write/seek? .IF NE UM$MU ;If Magtape BLT 100$ ;Branch if .SPFUN GETCQE R5 CALLR USRFN ;It's a USR function .ENDC ;NE UM$MU 100$: CMPB FUNTYP,#SF.BYP ;Is this the Bypass .SPFUN BNE 120$ ;No, check next function. 110$: DO BYPASS,THEN,UMEXIT ;Yes, do Bypass action and exit. 120$: .IF NE UM$DU ;If disk CMPB FUNTYP,#SF.SIZ ;Is this the volume sizer? BNE 130$ ;No, branch DO TRYONL,THEN,GETSIZ ;Do the Online request to getsize 130$: CALL FNDISP ;Call device specific dispatch .ENDC ;NE UM$DU .IF NE UM$MU ;If Magtape CMPB R4,#SF.MST ;Is this TS05 stream @100ips .SPFUN? BNE 140$ ;No 130$: CALLR FNDISP ;Jump to device specific dispatch .ENDC ;NE UM$MU 140$: .IF NE MMG$T ;If XM CALL BUSCHK ;Yes, go check bus limits on DMA BCC 150$ JMP HARDEX ;Branch if error .ENDC ;NE MMG$T 150$: GETCQE R5 .IF NE UM$DU ;If disk DO IOXFER,THEN,UMEXIT ;Yes, do I/O transfer then exit. .ENDC ;NE UM$DU .IF NE UM$MU ;If Magtape CALLR RWRT ;Go handle reads and writes (and most ; .SPFUNs). RWRT sets up the ;equivalent of the DO macro .ENDC ;NE UM$MU .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 it 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 - Starts the Controller! ;+ ; Send the command buffer to the port (ie. start the polling). ;- .ENABL LSB POLL: .IF NE UM$MU ;If magtape DEC (PC)+ ;We expect another interrupt INTEXP: .WORD 0 ; Minus count of interrupts expected. ABPOLL: ;Entry for Abort command: INTEXP was ; decremented already. .IF NE MU$FSM ;If file structured MOV ABTFLG,-(SP) ;Save Abort flag. (Avoids race: abort ; cmd interrupts fast, e.g. before the ; abort check below for RESTOR regs.) .ENDC ;NE MU$FSM .ENDC ;NE UM$MU MOV R0,-(SP) .IF EQ UM$ERL-2 MOV R3,-(SP) MOV .PCTAB,R3 MOV PC.ADR(R3),R0 ;Get pointer to response ring because MOV @R0,R0 ;command ring immediately follows MOV (SP)+,R3 ;the response ring. .IFF ;EQ UM$ERL-2 MOV #MRNG0,R0 $REL .-2 MRNG0 UMX .ENDC ;EQ UM$ERL-2 ADD #MRNGLN+2,R0 ;CRNGx immediately follows MRNGx BIC #FLAG,@R0 ;give command descriptor to the BIS #OWN,@R0 ;controller, but with the flag bit MOV (SP)+,R0 MOV @UDAIP,R5 ;off so that there are no UQSSP ;command ring interrupts .IF NE UM$MU ;If magtape .IF NE MU$FSM ;If file structured TST (SP)+ ;Are we Aborting? BNE 10$ ;If NE, yes, don't muck with registers RET2LO RESTOR ;Return, we'll be back on interrupt. .ENDC ;NE MU$FSM .ENDC ;NE UM$MU 10$: CALLR HRET ;Return, we'll be back on interrupt. HINIST: CALLR INISTP ;'HELP! I need somebodys' help.' ; - the Beatles .DSABL LSB .SBTTL .DRAST - Interrupt Entry Point ;+ ; Interrupt and abort entry points ; ; Output: R5 -> Response buffer ;- .ENABL LSB .IF NE MMG$T ;********************************************************************* UMPSECT ;********************************************************************* .ENDC ;NE MMG$T .IF NE UM$DU ;If disk .DRAST UM,UM$PRI ;Set up the interrupt entry. CLR R5 ;port 0 interrupted, so clear R5 BR 10$ .IF GE UM$PORTS-2 .DRAST U1,UM$PRI MOV #1,R5 ;port 1 interrupted BR 10$ .IF GE UM$PORTS-3 .DRAST U2,UM$PRI MOV #2,R5 ;port 2 interrupted BR 10$ .IF GE UM$PORTS-4 .DRAST U3,UM$PRI MOV #3,R5 ;port 3 interrupted BR 10$ .ENDC ;GE UM$PORTS-4 .ENDC ;GE UM$PORTS-3 .ENDC ;GE UM$PORTS-2 10$: .ENDC ;NE UM$DU .IF NE UM$MU ;If magtape UM$ABRT: .IF NE MMG$T ;*** Fetch/Load replaces next instr. w/NOP!!! .ASSUME . EQ UM$ABRT ;XM Abort entry NOP/RETURN moved. RETURN ;Becomes NOP when hi region init'ed .ENDC ;NE MMG$T LOW2HI UM$ABO ;Go handle abort .DRAST UM,UM$PRI,UM$ABRT ;Set up interrupt and abort entries. CLR R5 ;port 0 interrupted, so clear R5 BR 10$ .IF GE UM$PORTS-2 .DRAST U1,UM$PRI,UM$ABRT MOV #1,R5 ;port #1 interrupted BR 10$ .IF GE UM$PORTS-3 .DRAST U2,UM$PRI,UM$ABRT MOV #2,R5 ;port #2 interrupted BR 10$ .IF GE UM$PORTS-4 .DRAST U3,UM$PRI,UM$ABRT MOV #3,R5 ;port #3 interrupted BR 10$ .ENDC ;GE UM$PORTS-4 .ENDC ;GE UM$PORTS-3 .ENDC ;GE UM$PORTS-2 10$: .ENDC ;NE UM$MU .IF NE MMG$T .P1EXT P1HIGH ; (Map to high memory) CALL @#INTEX ;Enter interrupt in XM $REL .-2 INTEX UMX ; ... .P1END ; (End of block) .IF NE UM$DU ;If disk .BR FORKIT ;Do the FORK .ENDC ;NE UM$DU .IFF ;NE MMG$T .IF NE UM$MU ;If magtape CALL INTEX ;Enter interrupt .ENDC ;NE UM$MU .IF NE UM$DU ;If disk .BR INTEX ;Enter interrupt .ENDC ;NE UM$DU .IFTF ;NE MMG$T .IF NE UM$MU ;If magtape TST R5 ;What'll it be, pardner? BEQ FORKIT ;Normal stuff, do the .FORK BPL 20$ ;End of abort sequence, do .DRFIN RETURN ;Return, wait for last abort interrupt 20$: CALLR $DONE ;Abort is done, return queue element. .ENDC ;NE UM$MU .IFT ;NE MMG$T ;******************************************************************** UMXPSECT ;******************************************************************** .ENDC ;NE MMG$T .ENABL LSB INTEX: MOV #INTS0,R4 ;find the appropriate interrupt $REL .-2 INTS0 UMX ;indicators .IF EQ UM$ERL-2 .IF NE UM$PORTS-1 MOV R5,-(SP) ;save the port number 10$: DEC R5 ;use the port number to advance R4 BMI 20$ ;to the appropriate ADD #RINGLN,R4 BR 10$ 20$: MOV (SP)+,R5 ;restore the port number .ENDC ;NE UM$PORTS-1 .ENDC ;EQ UM$ERL-2 CLR 2(R4) ;clear response interrupt indicator .DSABL LSB ;convert the port number into a pointer into the port control table ;note that UM$PORTS=1 implies that R5=0 .IF NE UM$PORTS-1 ASL R5 .IF NE UM$ERL-2 .ASSUME PC.ESZ EQ 12 MOV R5,-(SP) ;save (*2) on stack ASL R5 ; *4 ASL R5 ; *8 ADD (SP)+,R5 ; *10. .IFF ;NE UM$ERL-2 .ASSUME PC.ESZ EQ 40 ASL R5 ; *4 ASL R5 ; *8 ASL R5 ; *16. ASL R5 ; *32. .ENDC ;NE UM$ERL-2 .ENDC ;NE UM$PORTS-1 ADD #PCTAB,R5 $REL .-2 PCTAB UMX .IF NE UM$PORTS-1 MOV R4,-(SP) MOV R5,-(SP) MOV #PORTNM,R4 $REL .-2 PORTNM UMR MOV PC.NUM(R5),R5 .HP1EX P1LOW ;set up PORTNM for FORKIT: MOV R5,@R4 .HP1EN MOV (SP)+,R5 MOV (SP)+,R4 .ENDC ;NE UM$PORTS-1 .IF NE UM$MU ;If magtape, then check if we are MOV R5,R4 ;aborting. CKABRT takes R4 -> entry in ;port control table; returns result CALL CKABRT ;in R5 RETURN ;then back to low memory. .ENDC ;NE UM$MU .IF NE UM$DU .IF NE MMG$T RETURN ;Back to low memory for FORK .IFF ;NE MMG$T .BR FORKIT ;Do the FORK .ENDC ;NE MMG$T .ENDC ;NE UM$DU .IF NE MMG$T ;******************************************************************** UMPSECT ;******************************************************************** .ENDC ;NE MMG$T .ENABL LSB FORKIT: ;.FORK in low memory .IF NE UM$MU MOV R4,R5 ;restore pointer to port control table .ENDC ;NE UM$MU .IF EQ UM$PORTS-1 .FORK UMFBL0 ;Well fork queue buddy! .IFF ;EQ UM$PORTS-1 BR 10$ PORTNM: .WORD 52525 10$: DEC PORTNM ;PORTNM comes back from INTEX equal BPL 20$ ;to the port number .FORK UMFBL0 BR 60$ 20$: .IF GE UM$PORTS-2 DEC PORTNM BPL 30$ .FORK UMFBL1 BR 60$ 30$: .IF GE UM$PORTS-3 DEC PORTNM BPL 40$ .FORK UMFBL2 BR 60$ 40$: .IF GE UM$PORTS-4 DEC PORTNM BPL 50$ .FORK UMFBL3 BR 60$ .ENDC ;GE UM$PORTS-4 .ENDC ;GE UM$PORTS-3 .ENDC ;GE UM$PORTS-2 50$: CALLR UMHERR ;should never get here, anyway 60$: .ENDC ;EQ UM$PORTS-1 LOW2HI INTEX2,.BR,PSECT=YES ;Enter rest of interrupt processing INTEX2: MOV R5,R3 ;R3 shall be the pointer to the ;appropriate entry in the PCTAB TST PC.STEP(R3) ;Are we in the middle of a four BPL HINIST ;step init? Branch if yes. MOV @PC.ASA(R3),R4 MOV R4,SAREG ;Yes: log an SAR Packet. ;we have already forked, so a memory ;location can be used BIT #,R4 ;Has port errored or been reset? BEQ 70$ ;No, skip next .IF NE UM$ERL CALL CNTERR .ENDC ;NE UM$ERL CALLR INIT ;Try to re-init the port 70$: .IF EQ UM$ERL-2 .ENABL LSB ;now make R4->point to the tail pointer of the appropriate ring ;and make R5->point to the buffer with the message MOV PC.ADR(R3),R4 MOV PC.BUF(R3),R5 ;R5 -> to the port's set of buffers MOV #TLP0,R2 $REL .-2 TLP0 UMX ;R2 -> to the appropriate tail pointer MOV PC.NUM(R3),R0 ;recall that the tail pointer is 10$: DEC R0 ;actually an offset BMI 20$ ADD #RINGLN,R2 BR 10$ 20$: MOV @R2,R0 ASR R0 ;divide tail pointer by 4 to get ASR R0 ;a buffer number 30$: DEC R0 ;make R5 -> to the appropriate buffer BMI 40$ ADD #P.MSIZ+ENVLEN,R5 BR 30$ 40$: .DSABL LSB .IFF ;EQ UM$ERL-2 MOV #VADR0,R4 ;If not MSCP error logging, there is $REL .-2 VADR0 UMX ;only one ring and one buffer MOV #MBUFF0,R5 $REL .-2 MBUFF0 UMX .BR ENDMSG ;If not MSCP error logging, it must be ;an end message. .ENDC ;EQ UM$ERL-2 .DSABL LSB .ENABL LSB .IF EQ UM$ERL-2 MOV MSGTYP(R5),R2 ;upper half of descriptor BNE 1776$ ;Branch if it's a good packet CALL GETNEXT ;Else dump it and get next packet RETURN 1776$: BIC #^C,R2 ;get the two status bits BEQ ENDMSG ;Branch if there is an end message. ;The message is a datagram (error log) destined to be logged. ;If it is for BBR algorithm I/O, the error log may need modification. BIT #IGN.IO,P.CRF(R5) ;should this datagram be ignored? BEQ 5$ MOV P.STS(R5),-(SP) ;if it is data error, ignore it BIC #^c,@SP CMP #ST.DAT,(SP)+ BEQ 20$ 5$: .IF NE DU$BBR BIT #BBR.IO,P.CRF(R5) ;is the datagram one that needs to BEQ 10$ ;be modified according to the CALL PAKMOD ;BBR algorithm 10$: .IF EQ UM$ERL-2 MOV L.EVNT(R5),R2 ;Save the event code of this datagram MOV #ORGCOD,R1 ;in case the BBR algorithm needs it $REL .-2 ORGCOD UMY ;for the CAUSE field of a BBR Attempt .HP1EX P1UMY ;Packet MOV R2,@R1 .HP1EN .ENDC ;EQ UM$ERL-2 .ENDC ;NE DU$BBR CALL LOGDGM 20$: CALL GETNEXT ;GETNEXT may invoke ENDMSG. RETURN .ENDC ;EQ UM$ERL-2 ; ; Handle an end message ; ENDMSG: .IF NE DU$BBR ;if doing BBR, then save the ring pointers so that if the BBR ;algorithm does I/O, if can restore these pointers appropriately ;for a call to GETNEXT .IF EQ UM$ERL-2 MOV R2,-(SP) MOV P.STS(R5),R2 MOV #ACTBBR,R1 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV @R1,R1 MOV R5,BB.BUF(R1) ;save virtual addr of response buffer MOV R4,BB.RNG(R1) ;save virtual addr of UQSSP ring MOV R3,BB.POR(R1) ;save addr of port control table entry MOV R2,BB.COD(R1) .HP1EN MOV (SP)+,R2 .ENDC ;EQ UM$ERL-2 TST BBRON ;Is BBR in progress ? BPL 40$ ;No, branch MOV #CONBBR,-(SP) ;here if BBR algorithm in progress $REL .-2 CONBBR UMY ;so go to the continue-BBR entry point JMP UMYJMP ;Go - get back into BBR code .ENDC ;NE DU$BBR 40$: .IF NE UM$MU ;If Magtape CLR R0 ;Clear Bypass recovery flag .ENDC ;NE UM$MU CMP INTEOP,#IOP.NR ;Is an internal operation with no BNE 50$ ;recovery? Branch if not. .IF NE UM$DU CMPB P.OPCD(R5),# ;End msg for DU Online cmd? BNE 41$ ;BR if not. MOV P.STS(R5),R1 BIC #^c,R1 ;Was Online successful? BNE 41$ ;BR if not. MOV R2,-(SP) ;Save R2 - R3. MOV R3,-(SP) MOV .UTTAB,R1 ;Get ptr(current UTTAB entry). MOV #UTTAB,R2 ;Get ptr(UTTAB). $REL .-2 UTTAB UMX MOV #PSTAB,R3 ;Get ptr(PSTAB). $REL .-2 PSTAB UMX .ASSUME UT.UNIt EQ 0 ;Update PSTAB. 45$: CMP @R1,(R2)+ ;Does MSCP unit # match? BNE 47$ ;BR if not: skip PSTAB entry. .ASSUME UT.PORt EQ > CMPB UT.PORt(R1),1(R2) ;Does port # match? BNE 47$ ;BR if not: skip PSTAB entry. .ASSUME UT.PARt EQ CMPB @R2,P.UNSZ+2(R5) ;Does unit have partition? BHI 47$ ;BR if not: no such partition. BEQ 46$ ;BR if last partition on unit. MOV #177777,@R3 ;Full partition: update PSTAB. BR 47$ 46$: MOV P.UNSZ(R5),@R3 ;Partial partition: zero base DEC @R3 ;last LBN accessible. .ASSUME UT.ESZ EQ > 47$: TST (R2)+ ;Advance to next UTTAB entry. TST (R3)+ ;Advance to next PSTAB entry. CMP R2,#UTTBND ;End of UTTAB? $REL .-2 UTTBND UMX BNE 45$ ;BR if not. MOV (SP)+,R3 ;Restore R2 - R3. MOV (SP)+,R2 .BR 41$ ;PSTAB upated for MSCP unit ;just brought Online. .ENDC ;NE UM$DU 41$: JMP GO$ON ;go straight to post interrupt ;activity 50$: TST INTEOP ;Is this an internal operation with BNE 120$ ;recovery? Branch IF IT IS! ;here if it is not an internal operation ;get function byte from queue element and see if this a BYPASS Spfun GETCQE R1 ;R1 -> current queue element. MOVB Q$FUNC(R1),FUNTYP .IF NE UM$N64 ;remove high three bits of unit number MOV R0,-(SP) MOV @#$SYPTR,R0 BIT #CF3.64,$CNFG3(R0) ;Is this a 64-unit system? BEQ 60$ ;branch if not, otherwise BISB #U64MSK,FUNTYP ;make FUNTYP look like a spfun code 60$: MOV (SP)+,R0 .ENDC ;UM$N64 CMPB #SF.BYP,FUNTYP ;are we handling a Bypass .SPFUN BNE 120$ ;No, then carry on. ;here if this is an BYPASS Spfun ;now we must copy the end message for the bypass ;back to the user's address space using $BLKMV MOV R5,-(SP) MOV R4,-(SP) MOV R3,-(SP) MOV R2,-(SP) MOV R1,-(SP) MOV R0,-(SP) 70$: MOV R5,R2 ;make R2 point to envelope+endmessage SUB #ENVLEN,R2 MOV Q$BUFF(R1),R4 ;R4 points to the user's buffer MOV #P.CSIZ+ENVLEN,R5 ;user allocated P.CSIZ bytes for each ASR R5 ;of the command buffer and the ;response buffer .IF NE MMG$T ;Note that since the buffers are in high memory, R2 must now be ;greater than 20000 MOV Q$MEM(R1),R3 MOV @#KISAR1,R1 80$: CMP R4,#20100 ;We must normalize PAR1 bias/offset BLO 90$ ;pairs for BLKMOV to work SUB #100,R4 INC R3 BR 80$ 90$: CMP R2,#20100 BLO 100$ SUB #100,R2 INC R1 BR 90$ 100$: MOV H$P1EX,R0 JSR PC,$BLMPT(R0) .IFF ;NE MMG$T 110$: MOV (R2)+,(R4)+ SOB R5,110$ .ENDC ;NE MMG$T MOV (SP)+,R0 MOV (SP)+,R1 MOV (SP)+,R2 MOV (SP)+,R3 MOV (SP)+,R4 MOV (SP)+,R5 CMP #1,@R1 ;Does the caller want BYPASS recovery? BNE GO$ON ;branch if Bypass with no recovery .IF NE UM$MU ;If Magtape COM R0 ;Yes, set Bypass recovery flag .ENDC ;NE UM$MU .IF NE MMG$T MOV R5,-(SP) GETCQE R5 MOV Q$MEM(R5),R1 ;Get user mapping MOV Q$BUFF(R5),R5 ;R5 -> start of cmd/res buffer ADD #ENVLEN,R5 ;R5 -> beginning of response CLR R2 ; (CLR + BISB to prevent sign extend) .HP1EX R1 ; (Start of P1EXT block) BISB P.FLGS(R5),R2 ;R2 = End message flags MOV P.STS(R5),R1 ;Get status .HP1EN ; (End of block) MOV (SP)+,R5 BR 140$ ;Go merge .ENDC ;NE MMG$T 120$: CLR INTEOP ;Reset internal op flag CLR R2 ; (CLR + BIS to prevent sign extend) BISB P.FLGS(R5),R2 ;R2 = End message flags .IF NE UM$MU ;If Magtape BITB #EF.SEX,R2 ;Serious Exception set? BEQ 130$ ;If EQ, no MOV .DNTAB,-(SP) ;@SP -> density table entry for unit ADD #DN.FLG,@SP ;@SP -> flag word in density table BISB #DN.CSE,@(SP)+ ;Must 'Clear Serious Excep' this unit .ENDC ;NE UM$MU 130$: MOV P.STS(R5),R1 ;R1 = status .IF EQ MMG$T MOV R1,STATU$ ;Store status for crash... .IFF ;EQ MMG$T 140$: .HP1EX P1LOW ; (Map to low memory) MOV R1,@#STATU$ ;Store status for crash... ('RELOC') $REL .-2 STATU$ UMR ; ... .HP1EN ; (End of block) .ENDC ;EQ MMG$T .IF NE UM$DU ;If doing Bad Block Replacement .IF NE DU$BBR BITB R2,#EF.BBR ;Bad block reported ? BEQ NOBBR ;No, branch .IF EQ UM$ERL-2 MOV R2,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV P.CRF(R5),R3 ;save the command reference num, low MOV P.CRF+2(R5),R4 ;save the command reference num, high MOV #ACTBBR,R2 $REL .-2 ACTBBR UMY .HP1EX P1UMY MOV (R2),R2 ;R2 -> current BBRTAB entry MOV R1,BB.COD(R2) ;save the status/event code and MOV R3,BB.CRF(R2) ;the command reference number of MOV R4,BB.CRF+2(R2) ;the command that detected a bad block .HP1EN MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 ;for the BBR attempted packet .ENDC ;EQ UM$ERL-2 MOV NEXT,AFTBBR ;save address of "real" post interrupt MOV #BBFND,-(SP) ;activity. Go do bad block replacement $REL .-2 BBFND UMY JMP UMYJMP SKPBBR: MOV AFTBBR,NEXT ;entry if unit was write protected MOV P.STS(R5),R1 ;or controller does BBR. NEXT could CLR R2 ;have been corrupted, so restore it BISB P.FLGS(R5),R2 ;and R1 and R2 NOBBR: .ENDC ;NE DU$BBR .ENDC ;NE UM$DU BIC #^c,R1 ;Isolate major status code bits. .ASSUME ST.SUC EQ 0 ;Did the I/O or function go ok? BNE 190$ ;No, then try "online". .IF NE UM$MU ;If magtape ;Magtape routine UMERR re-enters here ; on secondary position lost errors ; (Pos Lost but no Serious Exception.) CKCMD: CMPB #SF.MRD,LASCOM ;Yes, was last command a read? BNE 170$ ;No, check backup tape mark CMP RBCNT,P.BCNT(R5) ;Requested byte count = transferred? BNE 190$ ;No, treat it like an error 170$: INC BCKTM ;Last command 'backspace tape mark'? BEQ 190$ ;If EQ, yes, treat it like an error BITB #EF.EOT,R2 ;No, was End Of Tape detected? BNE 190$ ;Yes, treat it like an error .ENDC ;NE UM$MU GO$ON: ;here if the operation completed successfully .IF NE UM$ERL CALL LOGSUC ;log successfully complete I/O op .ENDC ;NE UM$ERL TST INTEOP ;internal operations with no recovery BMI 180$ ;do all their own dirty work CALL GETNEXT ;return current descriptor to ;controller 180$: CLR INTEOP ;Reset internal operation flag CALLR @NEXT ;I/O ok, do post interrupt activity. .IF NE UM$DU ;If disk 190$: CMP R1,#ST.AVL ;Was the unit Unit-Available? .IF EQ UM$ERL BNE 230$ ;No, since the unit was not Unit- ;-Available, the error is not ;recoverable, so take hard exit .IFF ;EQ UM$ERL BEQ 210$ ;Yes, try online because the unit ; is Unit-Available. Otherwise, ;since the unit was not Unit- ;-Available, the error is not ;recoverable, so call the error CALLR LOGBAD ;logger. LOGBAD will take hard exit .ENDC ;EQ UM$ERL .ENDC ;NE UM$DU .IF NE UM$MU ;If magtape 190$: MOV R4,-(SP) ;protect R4 from CDLCK CALL CDLCK ;Check if Cached Data was Lost MOV (SP)+,R4 ;restore the status from CDLCK CALL GETNEXT ;return descriptor to controller CMP R1,#ST.AVL ;is the unit in available state? BEQ 210$ ;Yes, try to bring it online. INC R0 ;No, see if it is Bypass recovery? .IF EQ UM$ERL BEQ 230$ ;Yes, just give hard error .BR 200$ ;No, go check some other errors .IFF ;EQ UM$ERL BNE 200$ ;No, go check some other errors CALLR LOGBAD ;Yes, log the bad I/O, take hard exit. .ENDC ;EQ UM$ERL 200$: CALLR UMERR ;Go check some errors .ENDC ;NE UM$MU 210$: .BR HSTART HSTART: CALL GETNEXT DEC RETRY ;Have we tried too much? BLE 220$ ;Yes, then its a hard error (init). CALLR BRINON ;Attempt to bring unit online 220$: .IF NE UM$ERL CALLR LOGBAD ;Log the end message if this is old ;non-mscp error logging .ENDC ;NE UM$ERL 230$: CALL GETNEXT CALLR UMHERR ;Issue hard error .SBTTL GETNEXT - increment tail (host's) pointer .ENABL LSB ; ;On input, R3 points to the ports entry in the control table ; R4 points to a pointer to the start of an UQSSP message ring ; R5 points to the packet that has just been processed GETNEXT:MOV R0,-(SP) MOV R1,-(SP) MOV R2,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV R5,-(SP) GNS = <.-GETNEXT>+2 ;GNS is the number of words this call ;put on the stack, used in case we pop it ;it is much easier to understand this code if you first ;look at the allocation of the rings and their buffers. ;make R0 point to the descriptor of the processed packet; i.e., ;R0 := the tail pointer 10$: MOV 2(R4),R0 ;Offset into ring: the TAIL POINTER. ADD @R4,R0 ;Make a pointer to the descriptor. MOV 2(R0),R1 ;get tail pointer, high order bits BMI 80$ ;if controller owned, then do nothing ;next two instructions effect: tail pointer := ((tail pointer + 4) ; mod (ring length)) TST (R4)+ ; advance R4 to tail pointer ADD #PTRLEN,@R4 .IF EQ UM$ERL-2 BIC #177760,@R4 ; do the MOD 16 .IFF ;EQ UM$ERL-2 BIC #177774,@R4 ; do the MOD 4 .IFT ;EQ UM$ERL-2 ;make R2 := the new tail pointer MOV @R4,R2 ; MOV 2(R4),R2 ADD -(R4),R2 ; ADD (R4),R2 MOV 2(R2),R1 ;Get high order part of new current ;descriptor, all we actually care ;about is the flag bits ;it is critical that we have obtained the status of the new current ;descriptor before we have returned the previous current descriptor .IFTF ;EQ UM$ERL-2 ;as soon as the next instruction completes, it possible for an ;interrupt to occur BIS #,2(R0) ;return previous current descriptor ;to the controller ;re-initialize the envelope MOV #P.MSIZ,PAKLEN(R5) ;define the buffer length MOV #VIR.ID,MSGTYP(R5) ;virtual circuit ID .IFT ;EQ UM$ERL-2 MOV PC.BUF(R3),R5 ;R5 must point to the ring's buffers ;now we examine the new current desciptor TST R1 BMI 50$ ;branch if not host owned ;in here iff there is a new packet to process 20$: MOV 2(R4),R2 ;get index into controller ring ASR R2 ;divide by four converts it into ASR R2 ;a buffer number ;R5 -> the buffer 30$: DEC R2 ;use the index to advance R5 to BMI 40$ ;the current buffer ADD #P.MSIZ+ENVLEN,R5 BR 30$ 40$: MOV MSGTYP(R5),R0 ;make sure it is a datagram BIC #^C,R0 BNE 45$ ;branch if it is a datagram ADD #GNS,SP ;remove return address and REGISTERS, JMP ENDMSG ;Go handle end message. 45$: .IF NE DU$BBR MOV L.EVNT(R5),R2 ;Save the event code of this datagram MOV #ORGCOD,R1 ;in case the BBR algorithm needs it $REL .-2 ORGCOD UMY ;for the CAUSE field of a BBR Attempt .HP1EX P1UMY ;Packet MOV R2,@R1 .HP1EN .ENDC ;NE DU$BBR CALL LOGDGM ;Log Error Log. BR 10$ ;here if the new current descriptor did not indicate a new packet 50$: MOV 2(R2),R1 ;check it again BMI 80$ ;branch if controller owned ;in here if a packet has come in since this invocation of GETNEXT- ;we must process this packet immediately if the ring was not empty ;when the packet was delivered; if, on the other hand, the ring was ;empty when the packet was delivered then there is an interrupt ;pending which will take care of this packet for us. We can tell ;if there is an interrupt pending by looking at the fork block ;because the fork block will be active (link field <> nil) iff ;an interrupt occurred for the new packet. .IF EQ UM$PORTS-1 .HP1EX P1LOW MOV @#UMFBL0,R1 ; Get link word in FORK block $REL .-2 UMFBL0 UMR .HP1EN .IFF ;EQ UM$PORTS-1 MOV PC.NUM(R3),R2 ; get port number, .ASSUME FKBKLN EQ 8. ASL R2 ; make offset for FORK blocks ASL R2 ASL R2 ADD #UMFBL0,R2 ; point to the right one $REL .-2 UMFBL0 UMR .HP1EX P1LOW MOV @R2,R1 ; get the FORK block link word... .HP1EN .ENDC ;EQ UM$PORTS-1 TST R1 ;look at the link field of the fork block. BNE 80$ ;exit because another thread of execution ;will take care of the new packet MOV PC.BUF(R3),R5 ;restore R5 BR 20$ ;process this new packet .IFTF ;EQ UM$ERL-2 80$: MOV (SP)+,R5 ; Restore all registers and return MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 .ASSUME <<.-80$>+2> EQ GNS .ENDC ;EQ UM$ERL-2 RETURN .DSABL LSB ;********************************************************************** ;********************** END OF INTERRUPT SERVICE CODE ***************** ;********************************************************************** .SBTTL SETCC - DO a SET CONTROLLER CHARACTERISTICS command ;+ ; SETCC ; 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. ;- SETCC: MOVB #-1,INTEOP ;MOVB instead of MOV to indicate ;internal Op WITH RECOVERY in progress MSCP OP.SCC,INT.IO ;'Set Controller Characteristics' CLR P.UNIT(R4) ;Reserved field for OP.SCC. MOV #TO.MIN*60.,P.HTMO(R4) ;Set the value .IF EQ UM$ERL-2 MOV #,P.CNTF(R4) ;enable error log datagrams - but .ENDC ;EQ UM$ERL-2 ;otherwise, no other flag is on RETURN .SBTTL ONLINE - Do Online Request for Retry or Sizing ;+ ; ONLINE, TRYONL ; ; The following routine will provide an online of the desired unit. ; ; For DISK, two entry points are available: one for internal oper- ; ations which desire to bring the unit up, the second to get the ; size of the drive indicated by the selected unit. ; ; For TAPE, the single entry point is to get the unit online and ; setup write-back caching if the unit supports it. The caching bit ; must be reset whenever the drive is put online. ; ; Destroys R5. ;- .ENABL LSB .IF NE UM$DU ;If disk TRYONL: MOV #IOP.NR,INTEOP ;Internal Op NO RECOVERY in progress .ENDC ;NE UM$DU ONLINE: MSCP OP.ONL,INT.IO ;Select the ONLINE command. .IF NE UM$MU ;If Magtape CALL CASHIT ;Setup caching if unit supports it. .ENDC ;NE UM$MU RETURN .DSABL LSB .SBTTL IGETUS - Do a GET UNIT STATUS Request ;+ ; For DU: 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. It may also be used by the 16-bit absolute LBN error processing ; ; For MU: The Get Unit Status is used to obtain information about the unit, ; caching support, enhanced re-write error recovery, and support for multiple ; tape densities. ;- IGETUS: MOV #IOP.NR,INTEOP ;Indicate INTERNAL op (no recovery) MSCP OP.GUS,INT.IO ;Select GET UNIT STATUS Command. RETURN ;Return! .SBTTL GETCBF - Initialize MSCP Command Buffer ;+ ; This subroutine clears the MSCP command buffer and inserts the generic ; parameters required by most commands. Recall that both the command and ; the message ring descriptors were initialized at port init time ; ; Output: R4 -> Command buffer ;- GETCBF: ;the command buffer immediately precedes the first message buffer ;there is always only one command buffer in the assembled handler MOV #MBUFF0,R4 $REL .-2 MBUFF0 UMX .ASSUME MBUFF0 EQ CBUFF0+P.CSIZ+ENVLEN MOV #-1,-(SP) ;Count words to clear on stack which TST -(R4) ;sub #4,R4 - now pointing at MBUFF's TST -(R4) ;envelope 15$: CLR -(R4) ;Clear out a word of the buffer. DEC @SP ;Whole buffer cleared (ie.=-1)? BPL 15$ ;No, keep clearing. TST (SP)+ .ASSUME P.CRF EQ 0 GETCQE 2(R4) ;build command reference number ; - high 16 bits are CQE address MOV CRFNUM,@R4 ;low 5-bits are stuffed below ADD #CRFINC,CRFNUM ;at this point R4 points to the command buffer .IF NE UM$MU CALL MUGCBF ;Handle magtape specifics .ENDC ;NE UM$MU ;at this point R4 still points to the command buffer MOV #P.CSIZ,PAKLEN(R4) ;Store size of command in envelope MOV #VIR.ID,MSGTYP(R4) .ASSUME LN.CMD EQ CBUFF0-ENVLEN ;stuff the opcode MOV (R5)+,P.OPCD(R4) ;Select the opcode. ;finish up the command reference number ADD (R5)+,P.CRF(R4) .IF EQ UM$ERL-2 MOV P.CRF(R4),QCHECK ;save the command reference number of MOV P.CRF+2(R4),QCHECK+2 ;the command we are about to issue .ENDC ;EQ UM$ERL-2 .IF NE UM$MU MOV P.CRF(R4),CUROPT ;same as above, but MU always needs MOV P.CRF+2(R4),CUROPT+2 ;the CRF .ENDC ;NE UM$MU .ASSUME UT.UNIT EQ 0 MOV @.UTTAB,P.UNIT(R4) ;Select the unit number. RTS R5 ;Return to the caller. .SBTTL BYPASS - Bypass RT-11 and Issue MSCP Directly ;+ ; BYPASS ; This subroutine implements special function BYPASS (code 371 and 360) ; which allows the user to issue MSCP commands directly to the disk ; system. ; ; Input: R4 points to the command buffer ; ; *** 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: GETCQE R5 .IF NE MMG$T ;copy command packet from user's program into the command buffer ;using $BLKMV MOV R3,-(SP) MOV R5,-(SP) MOV Q$MEM(R5),R1 MOV Q$BUFF(R5),R2 ;R2 := address of command envelope ADD #ENVLEN+P.CSIZ+ENVLEN,R2;we make R2 point to the command ;in the calling progam - but we know ;that the caller made the response ;buffer, which immediately precedes ;the command buffer, the same size ;as the command buffer. That is why ;we add in P.CSIZ instead of P.MSIZ 4$: CMP R2,#20100 ;make sure R1,R2 are a normalized pair BLO 5$ SUB #100,R2 INC R1 BR 4$ 5$: MOV @#KISAR1,R3 MOV #CBUFF0,R4 ;R4 := address of DU command buffer $REL .-2 CBUFF0 UMX ;$REL knows if we are in XM or not MOV R4,-(SP) ;protect R4 MOV #P.CSIZ,R5 ;copy the command buffer in the user's ASR R5 ;program down to the handler MOV H$P1EX,R0 JSR PC,$BLMPT(R0) MOV (SP)+,R4 ;R4 -> CBUFF0 MOV (SP)+,R5 MOV (SP)+,R3 .IFF ;NE MMG$T MOV Q$BUFF(R5),R2 ;R2 := address of command envelope ADD #ENVLEN+P.CSIZ+ENVLEN,R2;see explanation of above (XM) set ;up of R2 MOV #CBUFF0,R4 ;R4 := address of DU command buffer $REL .-2 CBUFF0 UMX ;$REL knows if we are in XM or not MOV R4,-(SP) MOV #P.CSIZ,R0 ASR R0 1$: MOV (R2)+,(R4)+ ;copy the command buffer from the SOB R0,1$ ;user's program down to the handler MOV (SP)+,R4 .IFTF ;NE MMG$T MOV #P.CSIZ,PAKLEN(R4) MOV #VIR.ID,MSGTYP(R4) .ASSUME P.CRF EQ 0 GETCQE 2(R4) ;build command reference number ; - high 16 bits are CQE address MOV CRFNUM,P.CRF(R4) ;low 5-bits are stuffed below ADD #CRFINC,CRFNUM .IF NE UM$MU ;If Magtape CLR LASCOM ;BYPASS mustn't confuse error recovery .ENDC ;NE UM$MU MOV Q$WCNT(R5),R0 ;Data buffer address specified? BEQ 20$ ;Nope, use the command message as is ;make a 22-bit buffer address, and put it in the Command packet .IFF ;NE MMG$T CLR R1 ;R1 = High order buffer address MOV R0,R2 ;R2 = Low order buffer address .BR 19$ .IFT ;NE MMG$T MOVB Q$JNUM(R5),R1 ;R1 = the job number BIC #^cJOBMK,R1 ASR R1 ;Get job number in low bits ASR R1 ; High byte is 0 means use ASR R1 ; U-D else I mapping MOV Q$WCNT(R5),R0 ;R0 = job's buffer's virtual address MOV R3,-(SP) ;$JBREL can trash R3 MOV H$P1EX,R2 CALL $CJVPT(R2) ;call $JBREL MOV (SP)+,R3 ;*C*restore R3 BCC 17$ ;Branch on success TST (SP)+ ;hard error. Must remove return addr. CALLR UMHERR ;for the JSR in COORD 17$: ;now have the R1 and R2 from $JBREL MOV #Q.BYP+Q.BLKN,R5 ;set up a fake queue element for a $REL .-2 Q.BYP+Q.BLKN UMX MOV R2,Q$BUFF(R5) ;call to MPPHY. MPPHY will convert MOV R1,Q$MEM(R5) ;the PAR1 bias/offset from $JBREL ADD #Q$BUFF-Q$BLKN,R5 ;into a 22-bit physical address MOV H$P1EX,R1 CALL $MPMEM(R1) MOV (SP)+,R2 ;R2 = low 16 bits of physical address MOV (SP)+,R1 ;R1 = high, but must be shifted right ASH #-4,R1 ;shift the high 6 bits of address .IFTF ;NE MMG$T 19$: MOV #CBUFF0,R4 ;R4 -> to the command packet $REL .-2 CBUFF0 UMX MOV R1,P.BUFF+2(R4) ;stuff the DMA address into the packet MOV R2,P.BUFF(R4) 20$: .IFT ;NE MMG$T ;make sure that the buffer address in the packet is UB mapped MOV P.BUFF(R4),R1 ;Get physical address out of packet MOV P.BUFF+2(R4),R2 ;in order to have UB map it for us. MOV #1,R3 ;R3 tells GETUMR we passing a PHY ADR MOV P.BCNT(R4),R0 ;GETUMR wants the word count so CLC ;right shift byte count ROR R0 MOV R4,-(SP) ;protect R4 .IF NE UM$DU .IF NE MMG$T MOV H$CQE,R4 ;R4 points to queue element .IFF ;NE MMG$T MOV UMCQE,R4 ;R4 points to queue element .ENDC ;NE MMG$T .ENDC ;NE UM$DU .IF NE UM$MU .HP1EX P1LOW ;(Map to low memory) MOV @#UMCQE,R4 ;R4 points to orig. queue element $REL .-2 UMCQE UMR ;... .HP1EN ;(End of block) .ENDC ;NE UM$MU MOV @#$SYPTR,R5 MOV $H2UB(R5),R5 CALL UB.GET(R5) ;allocate a temporary UMR MOV (SP)+,R4 ;*C* Restore R4 for POLL BCC 30$ TST (SP)+ ;UB wouldn't give us a UMR, so return RETURN ;to monitor, and we'll come back later 30$: MOV R1,P.BUFF(R4) MOV R2,P.BUFF+2(R4) .IFTF ;NE MMG$T RETURN ;Return (COORD will start operation) .IFT ;NE MMG$T Q.BYP: .REPT Q.ELGH ;a fake queue element for the AT call .WORD 0 .ENDR .ENDC ;NE MMG$T .DSABL LSB .PAGE .IF NE UM$ERL ;+ ; LOGSUC ; ; If success logging is enabled, log a successful I/O. ;- .ENABL LSB LOGSUC: TST $SUCS ;Is success logging enabled? BNE 10$ ;Branch if not. MOV R1,-(SP) ;Preserve R1 - R5. MOV R2,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV R5,-(SP) MOV #,R4 ;Set device and I/O success codes. BISB #GD.IO,R4 CALL LOGRQE ;Log successful I/O using the real ;queue element since the CRF number ;is defined. MOV (SP)+,R5 ;Restore R1 - R5. MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 MOV (SP)+,R1 10$: RETURN ;If success logging is not enabled, ;just return to caller. ;+ ; LOGBAD ; ; Subroutine to log fatal I/O errors for RT-11 error logging. ;- LOGBAD: .ENABL LSB .IF EQ UM$ERL-1 MOV R1,-(SP) ;Preserve R1 - R5. MOV R2,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV R5,-(SP) BIS #,RETRY ;Set device code into high byte. CALL LOGINI ;Prepare to log the end message. CALL LOGRQE ;Log end message using the real ;queue element since the CRF number ;is defined. MOV (SP)+,R5 ;Restore R1 - R5. MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 MOV (SP)+,R1 .ENDC ;EQ UM$ERL-1 CALL GETNEXT ;Make ring controller-available. CALLR UMHERR ;Take the error exit. .PAGE .IF EQ UM$ERL-2 ;+ ; LOGDGM ; ; Subroutine to log an error log. ; ; R3 -> the port control table entry ; R5 -> error log ; SAREG contains an SA CSR address ; ; ERLLOG is the entry point for LOGDGM from DU BBR routines. ; Entry from ERLLOG indicates that the error log has already been copied ; into ERLBUF in mapped environments. ;- .ENABL LSB ERLLOG: .IF NE MMG$T MOV #-1,LOGERL ;Indicate entry fron ERLLOG. .ENDC ;NE MMG$T LOGDGM: MOV R1,-(SP) ;Preserve R1 - R5. MOV R2,-(SP) MOV R3,-(SP) MOV R4,-(SP) MOV R5,-(SP) BIS #,RETRY ;Set device code in high byte ;of remaining retries (in low byte). CALL LOGINI ;Prepare ERLBUF. ;During MSCP error logging, there is a small possibility ;that we no longer have the RT11 I/O queue element for the I/O ;request associated with the queue element. We check for this case ;by comparing the command reference number in the packet, (which is ;composed of a counter and of the address of the original queue ;element), with the commnand reference number of the most recently ;dispatched command: QCHECK. QCHECK is initialized by POLL and ;cleared at .DRFIN time. If QCHECK and the datagram are equal then ;the queue element must be the appropriate queue element. If they ;do not agree, then we (taking no chances) send an "empty queue" ;element to the error logger so that the display indicates that the ;queue element was unknown. .HP1EX P1LOW MOV ENVLEN+P.CRF+2(R2),R5 ;datagram is in low memory, so use .HP1EN ;P1EXT to get the queue element addr. TST R5 ;Check for non-zero CRF in error log. BEQ 20$ ;BR if no CRF: use fake Q element. .HP1EX P1LOW MOVB ENVLEN+L.FLGS(R2),R1 ;Get flags field from error log. .HP1EN BITB #LF.INF,R1 ;Is error log informational? BNE 20$ ;BR if informational: use fake ;Q element. .HP1EX P1LOW MOV ENVLEN+L.EVNT(R2),R1 ;Get event code from error log. .HP1EN BIC #^c,R1 CMP R1,#ST.CNT ;Controller error? BEQ 30$ ;BR if yes: use real Q element CMP R1,#ST.INF ;Informational? BEQ 20$ ;BR if yes: use fake Q element. .HP1EX P1LOW MOVB ENVLEN+L.FMT(R2),R1 ;Get format field from error log. .HP1EN TSTB R1 ;Is it all clear? BEQ 30$ ;BR if yes: 10$: CMP QCHECK+2,R5 ;Is Q element address in CRF ;the same as in saved Q element ;address in QCHECK? BEQ 30$ ;BR if YES: use real Q element. 20$: CALL LOGFQE ;Use fake Q element. BR 40$ 30$: CALL LOGRQE ;Use real Q element. 40$: MOV (SP)+,R5 ;Restore R1 - R5. MOV (SP)+,R4 MOV (SP)+,R3 MOV (SP)+,R2 MOV (SP)+,R1 RETURN .PAGE .ENDC ;EQ UM$ERL-2 ; ; LOGINI ; Set up ERLBUF as necessary and set R2 - R4 in preparation for ; calling the error logger. ; .ENABL LSB LOGINI: MOV R5,R2 ;make R2 -> End Msg/Error Log. ;if we are in a mapped environment, the contents of the ;buffer are copied into the low memory buffer ERLBUF. .IF NE MMG$T ;+ ;*NOTE* The BLKMOV routine uses a PAR1 value and a PAR1 offset for both ; the input address and the output address to form the physical input address ; and the physical output address. Therefore, $REL has not produced the value ; in R4 appropriate for a call to BLKMOV; it has produced a low-memory ; physical address which we will then convert to a PAR1 bias,offset pair. ; ; $BLMPT is the offset from P1EXT to the BLKMOV routine ; for calling $BLMPT: R1,R2 are the input buffer ; R3,R4 are the output buffer ;- TST LOGERL ;have we already loaded ERLBUF? BNE 10$ ;branch if yes MOV R3,-(SP) ;protect R3 MOV @#KISAR1,R1 ;R1 = input buffer PAR1 value MOV #ERLBUF-ENVLEN,R4 ;R4 = output buff addr (PAR1 bias) $REL .-2 ERLBUF-ENVLEN UMR 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 SUB #ENVLEN,R2 ;if MSCP error logging, then start at ADD #ENVLEN/2,R5 ;UQSSP envelope, not the packet's unit MOV H$P1EX,R0 ;R0 -> P1EXT CALL $BLMPT(R0) ;Go move data to low memory MOV (SP)+,R3 ;restore R3 10$: CLR LOGERL ;reset this flag MOV #ERLBUF-ENVLEN,R2 ;R4 = output buff addr (PAR1 bias) $REL .-2 ERLBUF-ENVLEN UMR .IFF ;NE MMG$T SUB #ENVLEN,R2 .ENDC ;NE MMG$T MOV R2,R4 ;R4 will point to first word after packet .IF EQ UM$PORTS-1 MOV UDAIP,R3 .IFF ;EQ UM$PORTS-1 MOV PC.AIP(R3),R3 ;set up R3 for inside P1EXT block .ENDC ;EQ UM$PORTS-1 MOV SAREG,R5 ;set up R5 for inside P1EXT block .HP1EX P1LOW ;R2 is low memory (potentially) PAR1 pointer ADD @R2,R4 ;first word of envelope contains byte length ADD #ENVLEN,R4 MOV R3,@R4 ADD #2,R4 MOV R5,@R4 .HP1EN MOV $RETRY,R3 SWAB R3 .HP1EX P1LOW MOV @R2,R4 .HP1EN ROR R4 ADD #4,R4 ;add 4 for the CSR,SAR and Envelope BISB R4,R3 ;low byte of R3: word length of packet MOV RETRY,R4 ;Set device code/error logger action ;code into R4. RETURN .IF EQ UM$ERL-2 .ENABL LSB LOGFQE: ;Pass the fake queue element. MOV #QIO.NL,R5 ;Get its address in R5. $REL .-2 QIO.NL UMR CALL LOGERR RETURN .ENDC ;EQ UM$ERL-2 .ENABL LSB LOGRQE: ;Pass the real queue element. .HP1EX P1LOW MOV #UMCQE,R5 ;Get its address in R5. $REL .-2 UMCQE UMR MOV @R5,R5 .HP1EN CALL LOGERR RETURN .DSABL LSB .ENABL LSB LOGERR: ;Invoke the error logger. .IF NE MMG$T MOV H$ELPT,R1 ;call the error logger from P1EXT .HP1EX P1LOW ;because we may be in a PAR1 space CALL @R1 ;and the error logging system is .HP1EN ;entirely in low memory .IFF ;NE MMG$T CALL @$ELPTR .ENDC ;NE MMG$T RETURN .IF NE MMG$T LOGERL: .WORD 0 ;-1 ==> LOGDGM called from ERLLOG. .ENDC ;NE MMG$T .DSABL LSB .PAGE ;+ ;CNTERR - subroutine for logging a controller error - this routine makes ; a "Status Address Register" packet and sends it off to the error ; logger. The SAR packet consists of the contents of the SAR and ; the CSR of the dead port. ; ; INPUT: R3 -> to the port's entry in the port control table ; R4 = the Status Address Register's contents ;- .ENABL LSB CNTERR: MOV R5,-(SP) ;save registers MOV R4,-(SP) MOV R3,-(SP) MOV R2,-(SP) MOV R1,-(SP) MOV PC.AIP(R3),R3 ; R3 = the CSR address .HP1EX P1LOW MOV #ERLBUF,R2 ;the datagram buffer is in low memory $REL .-2 ERLBUF UMR CLR (R2)+ ;make an envelope of all zeros CLR (R2)+ MOV R3,(R2)+ ;send nothing but the CSR address MOV R4,(R2)+ ;and the contents of the SA register .HP1EN MOV #ERLBUF,R2 ;set up R2 for error logger call $REL .-2 ERLBUF UMR MOV $RETRY,R3 SWAB R3 BISB #/2,R3 ;set up R3 for error logger call MOV #UM$COD*400,R4 ;set up R4 for error logger call BISB RETRY,R4 ;low byte = remaining tries .HP1EX P1LOW MOV #UMCQE,R5 $REL .-2 UMCQE UMR MOV @R5,R5 ;R5 -> current queue element. .HP1EN .IF NE MMG$T MOV H$ELPT,R1 .HP1EX P1LOW CALL @R1 ;call the error logger from P1EXT .HP1EN ;because we may be in a PAR1 space .IFF ;NE MMG$T CALL @$ELPTR ;call the error logger .ENDC ;NE MMG$T MOV (SP)+,R1 ;restore registers MOV (SP)+,R2 MOV (SP)+,R3 MOV (SP)+,R4 MOV (SP)+,R5 RETURN .DSABL LSB .ENDC ;NE UM$ERL .IF NE MMG$T ;If XM .SBTTL BUSCHK - Check DMA Limits for a Given BUS ;+ ; ;- .ENABL LSB BUSCHK: MOV R5,-(SP) ;Save R5 MOV R3,-(SP) MOV @#$SYPTR,R3 ;R3 -> RMON base BIT #BUS$,$CNFG2(R3) ;Q-bus? BNE 20$ ;Yes, branch, no check needed. MOV H$CQE,R5 ;R5 -> current queue element (hi copy) 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 high bits into right place MOV (R5)+,-(SP) ;Store word count BPL 10$ ;Negative? NEG (SP) ;If so, make it positive 10$: ASL (SP) ;Make it into byte count ADD (SP)+,R1 ;Add byte count to low buffer address ADC R2 ; propaganda carry to high bits BIC #<^B11>,R2 ;Clear all but allowed 2 bits BNE 30$ ;Branch if more than 18 bits 20$: TST (PC)+ ;Clear Carry, skip next instr. 30$: SEC ;Set Carry MOV (SP)+,R3 ;*C* MOV (SP)+,R5 ;*C* Restore register RETURN ;*C* Return .DSABL LSB .ENDC ;NE MMG$T .SBTTL DOTAB - Routine to Return the Unit Translation Table ;+ ; DOTAB ; ; This routine implements special function 372 (DU) or 352 (MU). It returns ; the unit translation table to the caller if Q$WCNT is plus, and writes the ; provided table if the Q$WCNT is minus. If Q$WCNT = 1, then 16 words (plus ; two for the header) are returned; if Q$WCNT = -1, then it reads 16 words ; (plus two for the header) from the caller's buffer; otherwise, Q$WCNT equals ; the number of words to move. ;- .ENABL LSB DOTAB: MOV R0,-(SP) ;We need R0 as a worker. MOV R1,-(SP) MOV R5,R4 ;R4 -> queue element for putword. MOV #UMTAB,R5 ;R5 -> unit translation table. $REL .-2 UMTAB UMX ; $REL knows about XM vs. FB/SB MOV 2(R5),R0 ;make R0 = number of words in table INC R0 ;double, because 2 words per entry ASL R0 ;2 words for the header MOV Q$WCNT(R4),R1 ;Read table ? BMI 15$ ;No, branch ;in here to read table ;TST R1 ;is the word count=0, 0 is same BEQ 3$ ;case as 1 CMP #1,R1 ;If word count does not equal +1 then BNE 4$ ;it is number of words to transfer 3$: MOV #18.,R0 BR 5$ ;Q$WCNT equals the number of words 4$: CMP R1,R0 ;that the caller wants to receive BHI 5$ ;Only use Q$WCNT if it isn't more MOV R1,R0 ;than the actual length of the table .IF EQ MMG$T 5$: MOV Q$BUFF(R4),R1 10$: DEC R0 ;Decrement count BMI 40$ MOV (R5)+,(R1)+ ;Send a word to the user. BR 10$ ;Loop till all are transferred. .IFF ;EQ MMG$T 5$: DEC R0 BMI 40$ MOV (R5)+,-(SP) ;Put the word on the stack. CALL @H$PTWRD ;Send it to the user. NOTE: this ;call assumes R4 -> queue element BR 5$ ;Loop till all are transferred. .ENDC ;EQ MMG$T ;here to write a new table into UMTAB 15$: CMP R1,#-1 ;if word count doesn't equal -1, then BNE 18$ ;it equals the negation of the number MOV #-18.,R1 ;of words get from the caller 18$: NEG R1 CMP R1,R0 ;do not use Q$WCNT if it is more BHI 20$ ;than the actual length of the table MOV R1,R0 20$: ASL R0 ;Make wordcount into byte count .IF EQ MMG$T MOV Q$BUFF(R4),R1 30$: MOVB (R1)+,(R5)+ ;Store byte DEC R0 ;Decrement count BNE 30$ ;Loop till all are transferred .IFF ;EQ MMG$T 30$: CALL @H$GTBY ;Get byte from user MOVB (SP)+,(R5)+ ;Store the byte in table SOB R0,30$ ;Loop till all are transferred .ENDC ;EQ MMG$T 40$: MOV (SP)+,R1 MOV (SP)+,R0 ;Restore R0. CALLR DOFIN ;And take the all done exit because ;we do not need to call GETNEXT .DSABL LSB