Trailing-Edge
-
PDP-10 Archives
-
bb-x130a-sb
-
d8rint.mac
There are 4 other files named d8rint.mac in the archive. Click here to see a list.
TITLE D8RINT - SERVICE FOR DMR11 V006
SUBTTL T. LITT 15 NOV 83
;From D8KINT V026
SEARCH F, S ,NETPRM, D36PAR
$RELOC
$HIGH
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<
COPYRIGHT (C) 1981,1984 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
>
;
;DATE LOAD VERSION
;____ ____ _______
;
;19-JUL-83 70152 003
;23-AUG-83 70157 004
;18-OCT-83 70167 005
;15-NOV-83 70171 006
;
XP VD8RINT,006
Comment @ Loose ends.
o Worry about 2 lines to same node. Also worry about
"connected to self". (ie. lines connected together.)
End Comment @
D8RINT::ENTRY D8RINT
Comment @
D8RINT is the protocol translator between NETSER and DMRINT. NETSER
calls us at D8RDSP (thru the FEK), and we call NETSER at FEKINT.
End comment @
SUBTTL FEK -- FEK INTERFACE FOR D8RINT
;D8RINT FEK INTERFACE. ENTRIES ARE:
;
; D8RONC ;INITIALIZATION FOR THE FEK (Crank up DMR)
; D8RSEC ;ONCE A SECOND CODE.
; D8RRDD ;SET UP A READ REQUEST.
; D8RWRT ;QUEUE A BUFFER FOR OUTPUT.
; D8RCRS ;SHUT DOWN A LINE
; D8RDWN ;NOT USED
; D8RUP ;NOT USED
; D8RSTC ;CALLED WITH "U" := STC MESSAGE TO SEND
;
;FEKINT IN NETSER IS CALLED WHENEVER
;
; A DMR GOES OFFLINE (FF.DWN INTERRUPT)
; A MESSAGE IS RECEIVED (FF.IN INTERRUPT)
; AN XMIT MESSAGE IS ACKED (FF.OUT INTERRUPT)
D8RDSP::CAIL T1,FF.ONC ;RANGE CHECK THE FUNCTION
CAILE T1,FF.CPW ; CODE AND STOP IF BAD
PUSHJ P,NTDSTP## ;++ ERROR: BAD FUNCTION CODE TO FEK
JRST @.+1(T1) ;DISPATCH TO APPROPRIATE ROUTINE
JRST NTFONC## ;ONCE ONLY CODE (USE NETSER'S DEFAULT)
JRST NTFSEC## ;ONCE/SECOND CODE - NETSER's DEFAULT IS FINE
JRST D8RRDD ;SET UP A READ REQUEST
JRST D8RWRT ;SET UP A WRITE REQUEST (MESSY)
JRST D8RCRS ;CRASH THE FEK (CPU WENT DOWN?)
JRST CPOPJ## ;FEK DOWN (NOT USED)
JRST CPOPJ## ;FEK UP (NOT USED)
JRST D8RSTC ;CALLED WITH "U := STC MESSAGE TO SEND"
JRST D8RCRS ;SYSTEM SLEEPING AND
JRST D8RCRS ; WAKING IS TOO COMPLEX TO THINK ABOUT NOW
D8RCRS: PJSP T1,DMRERR## ;THIS SHOULD NEVER EVER HAPPEN. WE
; DO BETTER TIMING THAN NETSER. IF
; IT DOES HAPPEN, SOMETHING IS HORRIBLY
; WRONG.
D8RRDD: AOS FEKBSI(J) ;SET FLAG SAYING WE HAVE INPUT BUFFER
POPJ P, ;Regrettably, we can do nothing now
D8RWRT: NETOFF ;First, disable interrupts
SKIPG FEKOCT(J) ;If no messages,
JRST NTONPJ## ;Done
HRRZ T3,FEKOAD(J) ;GET THE ADDRESS OF THE NEXT MSG
HRRZ T1,PCBBLK(T3) ;GET THE ADDRESS OF THE ONE AFTER IT
HRRZM T1,FEKOAD(J) ;MAKE THE SECOND THE NEW FIRST
SKIPN T3 ;IF WE DIDN'T GET A MESSAGE,
PUSHJ P,NTDSTP## ;THE OUTPUT QUEUE IS MESSED UP
SOS FEKOCT(J) ;COUNT DOWN ONE LESS MESSAGE
NETON ;RE-ENABLE THE INTERRUPTS
SETZM MB.FMS(T3) ;MAKE SURE THIS LOOKS LIKE AN ANF MSG
MOVEI T1,KF.QOB ;GET THE QUEUE DATA FUNCTION
MOVE T2,FEKUNI(J) ;GET THE DMR BLOCK ADDRESS
PUSH P,T3 ;SAVE THE MSG POINTER
PUSHJ P,DMADSP## ;QUEUE THE MESSAGE
JRST [MOVEI T1,KI.OND ;GIVE THE NOTIFICATION OF
MOVE T2,J ; THE OUTPUT NOT DONE
POP P,T3 ;RESTORE MSG POINTER
PUSHJ P,D8RK2U ;DO WHATEVER NEEDED
JRST D8RWRT] ;CHECK FOR MORE
POP P,T3 ;RESTORE MSG POINTER
JRST D8RWRT ;AND GO CHECK FOR MORE
;D8RSTC ROUTINE TO HANDLE THE "FF.STC" FEK ENTRY.
;CALL U := POINTER TO STC BLOCK
;RETURN CPOPJ ;IF FEK IS "UP". IMPLIES A STC-REJECT
; CPOPJ1 ;STC MESSAGE IS QUEUED.
D8RSTC: SKIPGE T1,FEKBLK(J) ;GET THE FEK'S FLAGS
POPJ P, ;IF RUNNING, THEN DON'T TAKE THE LINE DOWN
TLNE T1,FK.MAI ;IF WE'RE IN MAINT MODE
JRST D8RST1 ; THEN WE CAN JUST QUEUE THE MESSAGE
MOVEI T1,KF.MAI ;IF NOT IN MAINT MODE, GET FUNCTION
MOVE T2,FEKUNI(J) ; TO PUT IN MAINT MODE, AND
PUSHJ P,DMADSP## ; CALL THE DRIVER.
POPJ P, ;COULDN'T DO ANYTHING, WE DON'T OWN LINE
MOVSI T1,FK.MAI ;GET THE MAINT BIT
IORM T1,FEKBLK(J) ; AND SAY WE'RE IN MAINT MODE
D8RST1: MOVEI T1,KF.QOB ;IF IN MAINT MODE, GET "QUEUE OUTPUT"
MOVE T2,FEKUNI(J) ; FUNCTION, AND PASS THE
MOVE T3,U ; BUFFER OFF TO THE DRIVER
SETZM MB.FMS(T1) ; MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
PUSHJ P,DMADSP## ;CALL DMRINT TO SEND THE MESSAGE
JRST [MOVEI T1,KI.OND ;GIVE THE NOTIFICATION OF
MOVE T2,J ; THE OUTPUT NOT DONE
MOVE T3,U ;SET UP MSG POINTER
PUSHJ P,D8RK2U ;DO WHATEVER NEEDED
JRST CPOPJ1] ;GIVE GOOD RETURN
RETSKP ; AND GIVE A GOOD RETURN
SUBTTL DMRINT - INTERFACE FROM DMR DRIVER
;D8RK2U HERE ON DISPATCH ENTRYS FROM THE DMR-11 KONTROLLER
;CALL T1 := FUNCTION CODE (BELOW)
; T2 := FEK/LINE-BLOCK ADDRESS
; T3 := BUFFER ADDRESS OR BYTE COUNT
D8RK2U::CAIL T1,KI.PRU ;FIRST RANGE CHECK THE
CAILE T1,KI.CLS ; FUNCTION CODE
PUSHJ P,NTDSTP## ;IF FUNCTION OUT OF RANGE, DUMP IT.
PUSH P,U ;SAVE AN AC
PUSH P,J ;AND ANOTHER AC
MOVE J,T2 ;USE J AS THE FEK ADDRESS
PUSHJ P,@D8RKKD(T1) ;DISPATCH BASED ON FUNCTION
POP P,J ;RESTORE ANOTHER AC
POP P,U ;RESTORE AN AC
POPJ P, ;BACK TO WHOMEVER
D8RKKD: IFIW D8PUP ;(00) PROTOCOL UP (START/STACK COMPLETE)
IFIW D8PDN ;(01) PROTOCOL DOWN (ALL BUFFERS RETURNED)
IFIW D8MAI ;(02) MAINT MSG RECEIVED (IMPLIES PDN)
IFIW D8STR ;(03) START RECEIVED (LEAVE MAINT PROTOCOL)
IFIW D8ODN ;(04) OUTPUT DONE (MESSAGE SUCCESSFULY SENT)
IFIW D8OND ;(05) OUTPUT NOT DONE (LINE IS GOING DOWN)
IFIW D8IDN ;(06) INPUT DONE (T3 := BUFFER ADDRESS)
IFIW D8RQI ;(07) REQUEST INPUT BUFFER (T3 := BYTE COUNT)
IFIW NTDSTP## ;(10) NEW LINE CREATION
IFIW NTDSTP## ;(11) OLD LINE DISSOLUTION
;D8PUP HERE WHEN PROTOCOL IS ESTABLISHED (START/STACK/ACK COMPLETE)
; MAKE SURE KONTROLLER IS HONEST AND THEN SET THE FEK ONLINE
D8PUP: SKIPGE FEKBLK(J) ;MAKE SURE THE FEK IS OFF-LINE
PUSHJ P,NTDSTP## ;IF IT'S ONLINE, KONTROLLER IS BUGGY
MOVSI T1,FK.MAI ;GET BIT AND CLEAR
ANDCAM T1,FEKBLK(J) ; MAINTENANCE MODE
MOVSI T1,FK.ONL ;GET BIT AND SET
IORM T1,FEKBLK(J) ; FEK-IS-ONLINE (WILL SOON QUEUE NODE-ID)
POPJ P, ;ALL DONE
;D8PDN HERE WHEN PROTOCOL IS TERMINATED (TIME-OUT OR WHATEVER)
; MAKE SURE WE ARE IN PROTOCOL, THEN CLEAR FEK-ONLINE
D8PDN: SKIPL FEKBLK(J) ;IF WE THINK THE FEK IS DOWN NOW,
POPJ P, ;THEN NO NEED TO CALL NETSER
MOVEI T1,FI.DWN ;GET THE "FEK CRASHED" FUNCTION CODE
PJRST CALNET ;AND TELL NETSER THE BAD NEWS
;D8MAI HERE WHEN WE RECEIVE A MAINTENANCE MESSAGE IN NORMAL PROTOCOL
; AT THIS POINT ALL OUTPUT BUFFERS HAVE BEEN RETURNED
D8MAI: SKIPGE FEKBLK(J) ;MAKE SURE WE DON'T THINK THAT
PUSHJ P,NTDSTP## ; WE ARE UP AND RUNNING.
MOVSI T1,FK.MAI ;GET AND SET THE
IORM T1,FEKBLK(J) ; MAINT-MODE BIT (SO WE ALLOC STC BLKS)
POPJ P, ;DONE. RESTORE AC'S AND LEAVE
;D8STR HERE WHEN WE RECEIVE A START MESSAGE WHILE IN MAINTENANCE PROTOCOL
; AT THIS POINT ALL OUTPUT BUFFERS HAVE BEEN RETURNED.
D8STR: ;The DMR is incapable of this.
PUSHJ P,NTDSTP
XLIST ;D8RINT code in case DMR is ever updated
REPEAT 0,<
MOVEI T1,KF.INI ;NOW TELL THE KONTROLLER
MOVE T2,FEKUNI(J) ; TO INITIALIZE ITSELF IN "NORMAL" MODE
PUSHJ P,DMADSP## ;DO IT
POPJ P, ;WE NO LONGER OWN THE LINE, NOTHING MATTERS
MOVSI T1,FK.MAI ;GET AND CLEAR THE MAINT
ANDCAM T1,FEKBLK(J) ; MODE BIT
POPJ P, ;RESTORE AC'S AND LEAVE
>;Repeat 0
LIST
;D8ODN HERE WHEN A BUFFER HAS BEEN SUCCESSFULLY OUTPUT
;D8OND HERE WHEN A BUFFER COULD NOT BE SENT (LINE DOWN ETC.)
; FIGURE OUT IF WE ARE IN MAINT/NORMAL MODE AND EITHER
; DESTROY OR RETURN THE BUFFER TO NETSER
D8ODN:
D8OND: MOVE T1,FEKBLK(J) ;GET THE FEK STATUS BITS
TLNE T1,FK.MAI ; AND IF WE ARE IN MAINT MODE
JRST D8MOD ; THEN GO FREE THE USLESS BUFFER
MOVEI T1,FI.ODN ;GET THE FEK CODE FOR OUTPUT DONE
HRRZM T3,FEKODN(J) ;PUT ADDRESS WHERE NETSER LIKES IT
PJRST CALNET ; AND RETURN THE BUFFER TO NETSER
D8MOD: MOVE J,T3 ;PUT ADDRESS OF STD IN "J"
PJRST GIVSTC## ; AND TELL NETSER TO FREE THE STORAGE
;D8IDN HERE WHEN A MESSAGE HAS BEEN RECEIVED (T3 := MESSAGE)
; DETERMINE WHICH MODE WE ARE IN (NORMAL/MAINT) AND FORWARD
; THE MESSAGE TO NETSER APPROPRIATLY
D8IDN: MOVE T1,FEKBLK(J) ;GET THE FEK'S STATUS BITS
TLNE T1,FK.MAI ;IF WE'RE IN MAINT MODE,
JRST D8IDNM ;THEN HANDLE MAINT MSGS
SOS FEKBSI(J) ;IN NORMAL MODE, SAY WE ARE NO LONGER BUSY
CAME T3,FEKIAD(J) ;MAKE SURE WE GOT THE RIGHT MESSAGE BACK
PUSHJ P,NTDSTP## ;WRONG MESSAGE RETURNED!
MOVEI T1,FI.RDD ;FUNCTION CODE IS READ-DONE
PJRST CALNET ;TELL NETSER
D8IDNM: MOVEI T1,FI.STC ;IF IN MAINT MODE, SAY IT'S MAINT DATA
MOVE U,T3 ;COPY THE POINTER INTO NETSER'S FAVORITE
PJRST CALNET ; REGISTER, AND CALL NETSER
;D8RQI HERE WHEN THE KONTROLLER WANTS US TO ALLOCATE SPACE FOR
; AN INCOMING MESSAGE. (SIZE OF MESSAGE IN T3)
D8RQI: MOVE T1,FEKBLK(J) ;GET THE FEK STATUS BITS, AND
TLNE T1,FK.MAI ; IF WE ARE IN MAINT MODE, THEN
JRST D8RQM ; GIVE THE KONTROLLER MAINT BUFFERS
SKIPE T1,FEKIAD(J) ;GET THE BUFFER (BUT IT MAY NOT BE THERE)
SKIPGE FEKBSI(J) ;MAKE SURE THERE WAS A BUFFER THERE
TDCA T1,T1 ;IF THERE WASN'T, SAY WE DIDN'T HAVE ONE
SETZM MB.FMS(T1) ;MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
POPJ P, ;CLEAN UP AND RETURN
D8RQM: MOVE T1,T3 ;COPY THE REQUESTED LENGTH
PUSHJ P,GETSTC## ;IF MAINT MODE, GET AN STC BLOCK
SETZ J, ; IF NONE AVAILABLE, RETURN ZERO
SKIPE T1,J ;PUT POINTER IN T1 (OR A ZERO IF NO BUFFER)
SETZM MB.FMS(T1) ;MAKE SURE IT LOOKS LIKE AN ANF-10 MSG
POPJ P, ; AND RETURN
SUBTTL CALNET - Routine to call FEKINT saving needed ACs
;CALNET - ROUTINE TO CALL FEKINT SAVING AND RESTORING W & F
CALNET: PUSH P,W ;SAVE DMR POINTER
PUSH P,F ;SAVE DMR POINTER
PUSH P,T4 ;SAVE THE BDL POINTER
PUSHJ P,FEKINT## ;CALL NETSER
POP P,T4 ;RESTORE BDL POINTER
POP P,F ;RESTORE LINE
POP P,W ;RESTORE DMR
POPJ P, ;RETURN.
PRGEND
TITLE DMRSER - USER INTERFACE FOR DMR CONTROL V002
SUBTTL T. LITT 21 NOV 81
;From D8KINT V026
SEARCH F, S, NETPRM, D36PAR
$RELOC
$HIGH
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1981,1983 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VDMRSER,002
Comment @ Loose ends.
End Comment @
DMRSER::ENTRY DMRSER
DMRMXQ==^D10 ;MAXIMUM NUMBER OF INPUT OR OUTPUT MSGS
; ALLOWED FOR A USER OF THE DMR: DEVICE
IOSMAI==400 ;IF SET, LINE SHOULD BE USED IN MAINT MODE
IOSSRM==200 ;IF SET, START WAS RECEIVED IN MAINT MODE
IOSMRN==100 ;IF SET, NORMAL MSG WAS RECEIVED IN MAINT MODE
DALBUG==1 ;IF TRUE, THEN WASTE LOTS OF TIME WORKING
; AROUND LEWINE'S MICRO-CODE BUG
;ERROR BITS RETURNED IN DEVIOS
; IOIMPM LINE NOW "OWNED" BY THE DMR DEVICE (ANF/DECNET CONTROLS IT)
; IODTER LINE NOT "STARTED" YET (ONLY HAPPENS IN "NORMAL" MODE)
; IODERR LINE WENT DOWN (USUALLY IOSSRM OR IOSMRN ARE SET AS WELL)
; IOBKTL BLOCK TO LARGE -- ONE WAY OR THE OTHER
SUBTTL DMRDDB - PROTOTYPE DDB FOR THE DMR DEVICE
DEFINE X(NAME<%%%OFF>,SIZE<1>),<NAME==:<%%%OFF==%%%OFF+SIZE>-SIZE>
%%%OFF==DEVLLD ;INITIALIZE THE OFFSET
X DEVDMR ;XWD DMR-ADDRESS, INPUT MSG QUEUE
X DEVOBC ;COUNT OF OUTPUT BUFFERS OUTSTANDING
X DMRDBL,0 ;LENGTH OF A DMR DDB
PURGE X ;FLUSH THE MACRO
;NOW LAY OUT THE PROTOTYPE DDB
DEFINE X(OFFSET,EXPR)< ;MACRO TO SET SELECTED LOCATIONS IN THE BLOCK
RELOC DMRDDB+OFFSET ;GO TO THE RIGHT WORD
EXPR ;ASSEMBLE IN THE EXPRESSION
>
$LOW ;WE NEED TO LINK THIS IN
DMRDDB::X DEVCHR,<XWD <6*HUNGST>+DVC2IO,DMRMMS+1> ;BUFFER SIZE
X DEVSER,<XWD 0,DMRUDS> ;DEFINE NETWORK DISPATCH VECTOR
X DEVMOD,<XWD DVIN!DVOUT,1_BYTMOD>
X DEVSTA,<XWD DEPLEN!.TYDMR,DEPEVM> ;VARIABLE BUFFERS, NO EVM
; X DEVCPU,<EXP 707B8> ;SET DEYPCL SO WILL RUN ON ANY CPU.
X NETLEN,0 ;RESERVE ENOUGH SPACE FOR ENTIRE DDB
$HIGH ;BACK TO PURE CODE
PURGE X ;FLUSH THE MACRO
SUBTTL DMRUUO -- UUO ENTRY TO DMRSRV
;DISPATCH TABLE (FROM UUOCON)
JRST CPOPJ## ;(-5) DEVICE OFF LINE
JRST CPOPJ## ;(-4) SPECIAL ERROR STATUS
JRST REGSIZ## ;(-3) LENGTH CAN BE GOTTEN FROM DDB
JRST CPOPJ## ;(-2) INITIALIZE (WE DO IT IN TSTDMR)
JRST CPOPJ## ;(-1) HUNG DEVICE
DMRUDS: JRST DMRREL ;(0) RELEASE (MAKE SURE DMR DOESN'T POINT)
JRST DMRCLO ;(1) CLOSE OUTPUT
JRST DMROUT ;(2) OUTPUT
JRST DMRIN ;(3) INPUT
;TSTDMR ROUTINE CALLED FROM UUOCON DURING A PHYSICAL DEVICE SEARCH
;CALL T1 := DEVICE NAME
;RETURN CPOPJ NOT DMR, NOT IN OPEN UUO, ALREADY ASSIGNED OR NO PRIVS
; CPOPJ1 F := DDB ADDRESS
TSTDMR::HLRZ T2,T1 ;GET THE LH OF THE NAME,
TRNN T1,007777 ;IF NOT A SINGLE DIGIT UNIT NUMBER
CAIE T2,'DMR' ; OR IF IT ISN'T "DMR"
POPJ P, ; THEN GIVE THE ERROR RETURN RIGHT AWAY
LDB T2,[POINT 9,M,8] ;GET THE OP-CODE OF THE UUO THAT GOT HERE
CAIE T2,<OPEN>_<-33> ;DON'T ALLOW RANDOM COMMANDS TO BUILD DDB'S
POPJ P, ;IF NOT AN OPEN UUO, THEN DEVICE NOT THERE
PUSHJ P,SAVT## ;SAVE THE TEAS
PUSHJ P,SAVE1## ; AND A PEA
MOVE P1,T1 ;STORE THE DEVICE NAME IN P1 FOR THE DURATION
MOVSI T1,JP.POK ;ONE LAST QUICK CHECK, DON'T ALLOW
PUSHJ P,PRVBIT## ; JUST ANYONE TO USE THE DEVICE.
CAIA ; ONLY OPR AND POKE ALLOWED.
POPJ P, ;STUPID BASS AKWARDS UUO
;NOW CHECK THE NAME
LDB T1,DRNDMR ;GET THE CONTROLLER NUMBER
SUBI T1,'0' ;Make it binary
SKIPGE T1 ;IF LEGAL
POPJ P, ;BAD
CAIG T1,M.DMRN##-1 ; AND MAKE SURE IT'S BOTH LEGAL
SKIPN W,DMRTBL##(T1) ; AND THAT IT EXISTS
POPJ P, ;IF IT DOESN'T EXIST, USER CAN'T OPEN IT
SKIPE DMRDDP(W) ;SEE IF SOMEONE ELSE OWN'S THE DDB
JRST CLRFPJ ; IF SO, ZERO F AND RETURN
;NOW BUILD A DDB
MOVEI T2,DMRDBL ;GET THE LENGTH OF A DMR DDB
PUSHJ P,GETWDS## ; AND ALLOCATE THE SPACE
JRST CLRFPJ ; IF NOT ENOUGH, GIVE THE FAIL RETURN
MOVSI T2,DMRDDB ;GET THE ADDRESS OF THE PROTOTYPE
HRR T2,T1 ; AND A COPY OF THE ADDRESS OF THE NEW ONE
DDBSRL ;LOCK THE DDB CHAIN
BLT T2,DMRDBL-1(T1) ;INITIALIZE THE NEW ONE
HRLM T1,DEVSER+DMRDDB ; AND SET UP THE LINKS
DDBSRU ;DDB IS IN PLACE, UNLOCK THE DDB CHAIN
HRLM F,DEVDMR(T1) ;MAKE THE DDB POINT TO THE DMR BLOCK
EXCH T1,DMRDDP(W) ;MAKE THE DMR BLOCK POINT TO THE DDB
SKIPE T1 ;JUST A PARANOID CHECK.
PUSHJ P,NTDSTP## ; I DON'T THINK THIS IS POSSIBLE
MOVE F,DMRDDP(W) ;GET THE ADDRESS OF THE DDB
MOVEM P1,DEVNAM(F) ;SET THE DEVICE'S NAME
RETSKP ; AND GIVE A GOOD RETURN TO DDBSRC
CLRFPJ: SETZ F, ;CLEAR "F" IF AN ERROR
POPJ P, ; AND RETURN
;DMRREL ROUTINE TO PROCESS THE "RELEASE" FUNCTION OF A DMR
;CALL F := ADDRESS OF THE DDB
;RETURN CPOPJ ;ALWAYS
DMRREL: PUSHJ P,DMRCKO ;SEE IF WE ARE THE LINE'S "OWNER"
JRST DMRRL1 ;IF NOT, DON'T MESS WITH THE KONTROLLER
MOVEI T1,KF.HLT ;GET THE "HALT" FUNCTION CODE
HLRZ T2,DEVDMR(F) ;GET THE ADDRESS OF THE DMR PAGE
PUSHJ P,DMODSP## ;CALL THE KONTROLLER. TELL HIM TO HALE
JFCL ;IF WE DON'T OWN THE LINE WE DON'T CARE
DMRRL1: SKIPE DEVOBC(F) ;ALL BUFFERS SHOULD BE RETURNED BY NOW
PUSHJ P,NTDSTP## ;SINCE THEY ARE RETURNED BY THE "HALT" CALL
HLRZ T1,DEVDMR(F) ;CLEAR THE DMR'S POINTER TO THE DDB
SETZM DMRDDP(T1) ; SO WE DON'T GET ANY MORE INTERRUPTS
;NOW FREE ANY MSGS ON THE INPUT QUEUE
DMRRL2: DMROFF ;PROBABLY DON'T NEED TO, BUT...
HRRZ T1,DEVDMR(F) ;GET THE NEXT INPUT MESSAGE
JUMPE T1,DMRRL3 ;IF NONE, THEN WE ARE DONE
HRRZ T2,MB.NXT(T1) ;GET THE MESSAGE AFTER THAT ONE
HRRM T2,DEVDMR(F) ; AND MAKE SURE WE GET IT NEXT
DMRON ;QUEUE IS CONSISTANT AGAIN
PUSHJ P,GIVDRB ;RETURN THE BUFFER
JRST DMRRL2 ; AND GO GET THE NEXT ONE
DMRRL3: DMRON ;ALL MESSAGES HAVE BEEN FREED
POPJ P, ; AND WE'RE DONE
;ZAPDMR ROUTINE TO DESTROY A DMR DDB
;CALL F := DDB ADDRESS
;RETURN CPOPJ
ZAPDMR::PUSHJ P,DMRREL ;FIRST "RELEASE" IT (IN CASE OF SWAP READ ERR)
MOVEI T2,DMRDDB ;GET THE STARTING DDB
ZAPDR1: MOVE T1,T2 ;FOLLOW THE DDB CHAIN
HLRZ T2,DEVSER(T1) ;NEXT DDB
SKIPN T2 ;MAKE SURE THAT THE
PUSHJ P,NTDSTP## ;++ DDB WENT AWAY?
CAIE T2,(F) ;IS THIS THE ONE
JRST ZAPDR1 ;NO CONTINUE
DDBSRL ;LOCK THE DDB CHAIN
HLRZ T2,DEVSER(F) ;GET THE NEXT DDB
HRLM T2,DEVSER(T1) ;REMOVE THE DDB LINKS
DDBSRU ;UNLOCK THE CHAIN
HRRZ T2,F ;GET THE ADDRESS OF THE DDB
MOVEI T1,DMRDBL ; AND IT'S LENGTH
PUSHJ P,GIVWDS## ;FREE THE STORAGE
POPJ P, ;AND WE'RE DONE.
;DMRIN ROUTINE TO PROCESS THE IN UUO FOR THE DMR DEVICE
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;ALWAYS
T5==T4+1 ;T5 FOR THE EXTEND INSTRUCTION
T6==T5+1 ;T6 FOR INDEXED MSD PTRS (DECNET)
IFN M-T6,<PRINTX ?DMRSER assumes that ACs M and T6 are the same
PRINTX ?Because it knows that M is OK to trash>
DMRIN: PUSHJ P,SAVE2## ;P1 = DEVIAD, P2 = INPUT MESSAGE
MOVSI S,IOBEG!IO ;CLEAR IOBEG SET "INPUT"
ANDCAB S,DEVIOS(F) ; FOR NO PARTICULARLY GOOD REASON
PUSHJ P,DMRCKM ;MAKE SURE WE'RE IN THE CORRECT MODE
KILOOP: PUSHJ P,DMRONL ;MAKE SURE THE DMR IS UP
POPJ P, ; IF NOT, RETURN WITH ERROR BITS SET
HRRZ P1,DEVIAD(F) ;GET THE ADDRESS OF THE INPUT BUFFER
SKIPE T1,P1 ;MAKE SURE THAT THERE IS ONE,
EXCTUX <SKIPGE (T1)> ; AND THAT IT IS EMPTY
POPJ P, ;IF NO EMPTY BUFFER, THEN RETURN
PUSHJ P,BRNGE## ;MAKE SURE THE BUFFER IS ADDRESSABLE
HRRZ P2,DEVDMR(F) ;GET THE ADDRESS OF THE INPUT MESSAGE
JUMPE P2,KIWAIT ;IF NO INPUT, GO WAIT FOR SOME
;NOW SET UP TO COPY THE DATA
EXCTUX <HLRZ T4,(P1)> ;GET THE LENGTH OF THE INPUT BUFFER (+1)
SUBI T4,1 ;GET ACTUAL LENGTH IN WORDS
LSH T4,2 ; AND CONVERT THAT TO BYTES
MOVSI T5,(POINT 8) ;MAKE A BYTE POINTER TO THE
HRRI T5,2(P1) ; USER'S INPUT BUFFER
MOVE T3,MB.FMS(P2) ;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
MOVE T1,MD.BYT(T3) ;FROM THE MSC, GET THE BYTE COUNT
MOVE T6,MD.ALA(T3) ;BYTE POINTER IS INDEXED BY T6
MOVE T2,MD.PTR(T3) ; AND THE BYTE POINTER
CAMGE T4,T1 ;MAKE SURE THAT THE DATA WILL FIT,
JRST K.BKTL ; IF NOT, THEN GIVE "BLOCK TOO LARGE"
MOVEI T4,3(T1) ;MAKE THE "DST" LENGTH BE THE SOURCE
TRZ T4,3 ; LENGTH ROUNDED UP SO AS TO ZERO FILL LAST WD
EXCTUU <HRRM T1,1(P1)> ;STORE THE BYTE COUNT FOR THE USER
IFE DALBUG,< ;D.A.LEWINE
PXCT 1,[EXTEND T1,[EXP MOVSLJ,0]] ;AND COPY THE DATA
PUSHJ P,NTDSTP## ;WE CHECKED... THIS SHOULDN'T HAPPEN
>
IFN DALBUG,< ;UNTIL THEY GET THE MICRO-CODE RIGHT...
JRST .+3 ;SKIP INTO THE MIDDLE OF THE LOOP
ILDB T4,T2 ;GET THE NEXT BYTE
EXCTUU <IDPB T4,T5> ;STORE THE NEXT BYTE
SOJGE T1,.-2 ;LOOP TILL ALL STORED
SETZ T3, ;CLEAR THE "TO COUNT"
>
;NOW DEQUEUE THE INPUT MESSAGE AND ADVANCE THE USER'S BUFFER
DMROFF ;KEEP INTERRUPT LEVEL OUT
HRRZ T1,MB.NXT(P2) ;GET THE ADDRESS OF THE NEXT MESSAGE
HRRM T1,DEVDMR(F) ; AND MAKE THAT ONE BE THE FIRST
DMRON ;RE-ENABLE INTERRUPTS
MOVE T1,P2 ;GET THE DRB ADDR BACK
PUSHJ P,GIVDRB ;FREE THE MESSAGE BLOCK
PUSHJ P,ADVBFF## ;ADVANCE THE USER'S INPUT BUFFER
POPJ P, ;IF WE'RE SUPPOSED TO STOP, RETURN TO UUOCON
JRST KILOOP ;OTHERWISE TRY TO DO MORE INPUT
;DMROUT ROUTINE TO PROCESS THE OUTPUT UUO FOR THE DMR DEVICE
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;ALWAYS
DMRCLO:
DMROUT: PUSHJ P,SAVE2## ;P1 := DEVOAD, P2 := DATA BUFFER ADDRESS
MOVSI S,IOBEG ;CLEAR IOBEG
ANDCAB S,DEVIOS(F) ; FOR NO PARTICULAR GOOD REASON
MOVSI S,IO ;GET AND SET
IORB S,DEVIOS(F) ; "OUTPUT" SO IOSETC WORKS RIGHT
PUSHJ P,DMRCKM ;CHECK THE MODE
KOLOOP: PUSHJ P,DMRONL ;MAKE SURE THAT THE DEVICE IS ONLINE
POPJ P, ;IF NOT, RETURN (WITH ERROR BITS SET)
HRRZ P1,DEVOAD(F) ;GET THE ADDRESS OF THE OUTPUT BUFFER
SKIPE T1,P1 ;MAKE SURE THAT THERE IS ONE, AND
EXCTUX <SKIPL (T1)> ; THAT IT HAS DATA IN IT
POPJ P, ;IF NO FULL BUFFER, THEN EXIT
PUSHJ P,BRNGE## ;MAKE SURE THE BUFFER IS ADDRESSABLE
AOS T1,DEVOBC(F) ;COUNT UP THE NUMBER OF BUFFERS OUTSTANDING
CAILE T1,DMRMXQ ;IF TOO MANY, THEN
JRST [SOS DEVOBC(F) ; TAKE BACK WHAT WE JUST SAID
JRST KOWAIT] ; AND WAIT FOR SOME TO GET SENT
;NOW ALLOCATE A DMR DATA BLOCK TO HOLD THE DATA
KOLOO1: EXCTUX <HRRZ T1,1(P1)> ;GET THE NUMBER OF USER BYTES
CAILE T1,DMRMMS*4 ;MAKE SURE THAT THE NUMBER IS REALISTIC
JRST [SOS DEVOBC(F) ;IF TOO MANY, TAKE BACK THE COUNT
JRST K.BKTL] ; AND TELL THE USER "BLOCK TOO LARGE"
PUSHJ P,GETDRB ;GET A BUFFER FOR THE MESSAGE
JRST [SOS DEVOBC(F) ;SINCE ^C CAN HAPPEN HERE...
MOVEI T1,2 ;IF NO CORE AVAILABLE,
PUSHJ P,SLEEPF## ;SLEEP FOR 2 SECONDS AND
JRST KOLOOP] ; TRY AGAIN
MOVE P2,T1 ;REMEMBER THE ADDRESS OF THE MESSAGE BLOCK
;NOW COPY THE DATA INTO THE DATA BLOCK
EXCTUX <HRRZ T1,1(P1)> ;GET THE USER'S BYTE COUNT BACK AGAIN
MOVSI T2,(POINT 8) ;BUILD A BYTE POINTER TO THE
HRRI T2,2(P1) ; USER'S DATA
MOVE T3,MB.FMS(P2) ;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
MOVE T4,T1 ;GET THE LENGTH
MOVEM T1,MD.BYT(T3) ;STORE THE NUMBER OF BYTES WE'RE GOING TO COPY
MOVE T5,MD.AUX(T3) ;GET THE ADDRESS OF THE FIRST BYTE
MOVE T6,MD.ALA(T3) ;MD.AUX IS INDEXED BY T6
IFE DALBUG,< ;D.A.LEWINE
PXCT 2,[EXTEND T1,[EXP MOVSLJ,0]] ;COPY THE DATA
PUSHJ P,NTDSTP## ;CAN'T HAPPEN
>
IFN DALBUG,< ;UNTIL THEY GET THE MICRO-CODE RIGHT...
JRST .+3 ;SKIP INTO THE MIDDLE OF THE LOOP
EXCTUX <ILDB T4,T2> ;LOAD THE NEXT BYTE
IDPB T4,T5 ; AND STORE IT IN THE MONITOR BUFFER
SOJGE T1,.-2 ;LOOP TILL ALL STORED
SETZ T3, ;CLEAR THE "TO COUNT"
>
MOVEI T1,KF.QOB ;FUNCTION = QUEUE OUTPUT DATA
HLRZ T2,DEVDMR(F) ; TO THIS DMR
MOVE T3,P2 ; AND THIS IS THE MESSAGE BLOCK
PUSHJ P,DMODSP## ;CALL THE KONTROLLER
JRST [MOVEI T1,KI.OND ;SIGNAL OUPUT NOT DONE
MOVE T2,F ;POINT TO DDB
MOVE T3,P2 ;POINT TO MSG BLOCK
PUSHJ P,DMRKTU ;GIVE THE INTERRUPT
JRST .+1] ; AND CONTINUE
PUSHJ P,ADVBFE## ;ADVANCE THE USER'S OUTPUT BUFFER
POPJ P, ;IF NO MORE OUTPUT, RETURN TO UUOCON
JRST KOLOOP ;OTHERWISE TRY TO SEND MORE
PURGE T5 ;DONE WITH EXTEND INSTRUCTIONS
;KIWAIT ROUTINE TO WAIT FOR INPUT
KIWAIT: MOVE T2,DEVAIO(F) ;GET THE ASYNCH IO BITS
HRRZ T1,DEVBUF(F) ;GET THE ADDRESS OF THE BUFFER CONTROL BLOCK
JUMPE T1,CPOPJ## ;IF NO BUFFERS SETUP ??
EXCTUX <HRRZ T1,(T1)> ;GET THE ADDRESS OF THE NEXT USER'S BUFFER
EXCTUX <SKIPL (T1)> ;IF HE HAS INPUT TO READ,
TRNE T2,DEPAIO ; OF IF THIS IS ASYNCH IO, THEN
POPJ P, ; RETURN TO THE USER
MOVEI T1,EV.DMR ;OTHERWISE, GO INTO
PUSHJ P,ESLEEP## ; EVENT WAIT AND THEN
JRST KILOOP ; CHECK FOR MORE INPUT
;KOWAIT WAIT FOR OUTPUT TO COMPLETE
KOWAIT: MOVE T1,DEVAIO(F) ;GET THE WORD WITH THE ASYNCH IO BITS
TRNE T1,DEPAIO ;SEE IF WE ARE DOING ASYNCH IO
POPJ P, ;IF ASYNCH IO, THEN RETURN TO THE USER
MOVEI T1,EV.DMR ;OTHERWISE, GO INTO
PUSHJ P,ESLEEP## ; EVENT WAIT, AND
JRST KOLOOP ; THEN SEE IF WE CAN DO OUTPUT
;DMRIAV ROUTINE TO SIGNAL THAT DMR INPUT IS AVAILABLE
DMRIAV: MOVE T1,DEVAIO(F) ;GET THE WORD WITH THE ASYNCH IO BITS
TRNN T1,DEPAIO ; AND IF THIS IS NOT ASYNCH IO,
JRST [LDB T1,PJOBN## ; ASSUME THAT THE GUY IS IN EVENT WAIT
PJRST EWAKE##] ; AND GO WAKE HIM
PUSH P,DEVIOS(F) ;IF ASYNCH IO, THEN MAKE
MOVSI S,IO ; SURE THAT THE "IO" BIT IS
ANDCAB S,DEVIOS(F) ; CLEAR. THIS INSURES THAT SETIOD
PUSHJ P,SETIOD## ; WILL GENERATE AN INPUT DONE INTERRUPT
POP P,DEVIOS(F) ;RESTORE THE STATE OF DEVIOS
POPJ P, ; AND RETURN.
;DMROAV ROUTINE TO SIGNAL THAT OUTPUT HAS BEEN COMPLETED
DMROAV: MOVE T1,DEVAIO(F) ;GET THE ASYNCH IO BITS
TRNN T1,DEPAIO ;IF WE ARE NOT DOING ASYNCH IO
PJRST [LDB T1,PJOBN## ; THEN ASSUME THAT THE GUY IS IN
JRST EWAKE##] ; EVENT WAIT AND WAKE HIM
PUSH P,DEVIOS(F) ;IF ASYNCH IO, THEN MAKE SURE THAT
MOVSI S,IO ; THE "IO" BIT SAYS OUTPUT. THIS
IORB S,DEVIOS(F) ; WILL CAUSE SETIOD TO GENERATE
PUSHJ P,SETIOD## ; AN "OUTPUT DONE" INTERRUPT.
POP P,DEVIOS(F) ;RESTORE DEVIOS AND
POPJ P, ; RETURN
;DMRCKO ROUTINE TO CHECK OWNERSHIP OF THE DMR BLOCK.
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;DMR IS NOT IN "PROGRAM" MODE
; CPOPJ1 ;DMR IS IN "PROGRAM" MODE
DMRCKO: HLRZ T1,DEVDMR(F) ;GET THE DMR PAGE ADDRESS
HRRZ T1,DMRUSR(T1) ;GET THE USER CODE
CAIN T1,DD.PRO ;IF IT IS "PROGRAM"
AOS (P) ; THEN GIVE A GOOD
POPJ P, ; RETURN
;DMRONL ROUTINE TO CHECK TO SEE IF THE DMR IN "ON LINE"
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;NOT OWNED OR NOT ONLINE (ERROR BITS SET)
; CPOPJ1 ;DMR APPEARS TO BE READY FOR I/O
DMRONL: PUSHJ P,DMRCKO ;FIRST SEE IF WE OWN THE LINE
JRST K.IMPM ; IF NOT, THEN IT'S IMPROPER MODE
HLRZ T1,DEVDMR(F) ;GET THE DMR BLOCK ADDRESS
EXCH W,T1 ;PUT ADDRESS IN RIGHT PLACE
LDB T2,PDRSTS## ;GET CURRENT STATE
EXCH W,T1 ;THUS
CAIGE T2,DR%MAI ;MUST BE IN AT LEAST MAINT STATE
JRST K.DERR ;IF NOT, SAY DEVICE ERROR
MOVE S,DEVIOS(F) ;FIRST GET THE DEVICE STATUS
TRNE S,IOSSRM!IOSMRN ; AND IF EITHER ERROR IS STILL LIT
JRST K.DERR ; RETURN "DEVICE ERROR"
TRNE S,IODERR!IODTER!IOIMPM!IOBKTL ;IF ANY "STANDARD" ERROR
POPJ P, ; FORCE THE USER TO CLEARR IT
TRNE S,IOSMAI ;IF WE ARE TRYING MAINT MODE,
JRST DMRON1 ; GO CHECK ONLINE DIFFERENTLY
CAIGE T2,DR%WT1 ;IF SUPPOSED TO BE NORMAL, BUT NOT
JRST K.DERR ; THEN WE MUST HAVE SCREWED UP
CAIE T2,DR%RUN ;IF WE'RE RUNNING
CAIN T2,DR%WT1 ;OR IF WE NEED A MESSAGE TO START
CAIA ;WE ARE OK
JRST K.DTER ; ELSE IT'S A "SOFT" ERROR. (WAIT FOR ONLINE)
RETSKP ;IF IN RUN, THEN ALL'S OK
DMRON1: CAIE T2,DR%MAI ;IF WE'RE NOT IN MAINT MODE, THEN
JRST K.DERR ; IT'S AN ERROR
RETSKP ;OTHERWISE WE'RE "ONLINE"
;DMRCKM ROUTINE TO CHECK/SET THE MAINT/NORMAL MODE OF THE LINE
;CALL F := DDB
;RETURN CPOPJ ;ALWAYS (STARTS THE LINE IN MAINT/NORMAL)
DMRCKM: PUSHJ P,DMRCKO ;SEE IF WE ARE THE LINE'S OWNER
POPJ P, ; IF NOT, DON'T DO ANYTHING
PUSH P,W ;FIRST PRESERVE THE DDB POINTER
HLRZ W,DEVDMR(F) ;FROM THAT GET THE DMR BLOCK ADDRESS
LDB T1,PDRSTS## ;FROM THAT, GET THE STATE
POP P,W ;NOW RESTORE THE DDB ADDRESS
MOVE S,DEVIOS(F) ;RELOAD THE IO STATUS
TRNE S,IOSMAI ;IF WE'RE SUPPOSED TO BE IN MAINT
JRST DMRCK1 ; THEN GO CHECK DIFFERENTLY
CAIL T1,DR%WT1 ;IF WE'RE TRYING TO START OR BETTER
POPJ P, ; THEN ALL'S OK
MOVEI T1,KF.INI ;WE WANT TO "INITIALIZE" THE LINE
JRST DMRCK2 ; SO GO TO COMMON CODE TO DO SO
DMRCK1: CAIN T1,DR%MAI ;IF WE'RE IN MAINT MODE
POPJ P, ; THEN IT'S OK
MOVEI T1,KF.MAI ;WE WANT TO PUT IN MAINT STATE
DMRCK2: PUSH P,T1 ;SAVE THE "DESTINATION STATE"
MOVEI T1,KF.HLT ;FIRST WE MUST "HALT" THE LINE
HLRZ T2,DEVDMR(F) ; GET THE LINE ID
PUSHJ P,DMODSP## ; AND CALL THE KONTROLLER
JFCL ;CAN'T BE, WE ALREADY CHECK THAT WE ARE OWNER
POP P,T1 ;NOW GET THE "MODE" BACK
HLRZ T2,DEVDMR(F) ; AND THE LINE ID
PUSHJ P,DMODSP## ;CHANGE THE STATE
JFCL ;CAN'T BE, WE ALREADY CHECK THAT WE ARE OWNER
POPJ P, ; AND RETURN
;ROUTINES TO SET VARIOUS ERROR BITS
K.IMPM: MOVEI S,IOIMPM ;GET THE IMPROPER MODE BIT
JRST K.SET ; AND GO SET IT
K.DTER: MOVEI S,IODTER ;GET "SOFT ERROR"
JRST K.SET ; AND SET IT
K.DERR: MOVEI S,IODERR ;GET THE DEVICE ERROR BIT
JRST K.SET ;AND SET IT
K.BKTL: MOVEI S,IOBKTL ;GET THE BLOCK TOO LARGE BIT
K.SET: IORB S,DEVIOS(F) ;SET THE APPROPRIATE BIT
POPJ P, ; AND RETURN
;DMRKTU INTERRUPT LEVEL DISPATCH FROM DMR-11 KONTROLLER
DMRKTU::CAIL T1,KI.PRU ;RANGE CHECK THE FUNCTION CODE
CAILE T1,KI.CLS ; BEFORE BLINDLY DISPATCHING ON IT
PUSHJ P,NTDSTP## ;++ KONTROLLER GAVE BAD FUNCTION?
PUSH P,F ;SAVE THE DMR PAGE ADDRESS (OR WHAT EVER)
MOVE F,T2 ;SET F TO POINT TO THE DDB
PUSHJ P,@DMRID1(T1) ;CALL THE APPROPRIATE INTERRUPT ROUTINE
POP P,F ;RESTORE "F"
POPJ P, ; AND RETURN TO THE KONTROLLER
DMRID1: IFIW DR.PUP ;(00) PRIMARY PROTOCOL UP
IFIW DR.PDN ;(01) PRIMARY PROTOCOL DOWN
IFIW DR.MAI ;(02) MAINT MSG RECEIVED IN NORMAL PROTOCOL
IFIW DR.STR ;(03) START MSG RECEIVED IN MAINT PROTOCOL
IFIW DR.ODN ;(04) OUTPUT DONE
IFIW DR.OND ;(05) OUTPUT NOT DONE
IFIW DR.IDN ;(06) INPUT DONE
IFIW DR.RQI ;(07) REQUEST INPUT BUFFER
IFIW NTDSTP## ;(10) LINE CREATION
IFIW NTDSTP## ;(11) LINE DISSOLUTION
;PROTOCOL UP
DR.PUP: MOVEI S,IODERR ;GET THE IO DEVICE ERROR BIT
ANDCAB S,DEVIOS(F) ; AND CLEAR IT
PUSHJ P,PSIONL## ;SIGNAL THE DEVICE IS ONLINE
JRST DR.WAK ; AND WAKE UP ANY SERVICE ROUTINES
;PROTOCOL DOWN
DR.PDN: PUSHJ P,K.DERR ;SIGNAL AN IO DEVICE ERROR
PUSHJ P,PSIDWN## ; AND GIVE AN OFFLINE INTERRUPT
DR.WAK: PUSHJ P,DMRIAV ;NOW MAKE SURE THAT THE SERVICE ROUTINE
PUSHJ P,DMROAV ; WAKE UP SO THAT THE SEE THE
LDB T1,PJOBN## ;GET THE JOB NUMBER
PUSHJ P,WAKPST## ; AND WAKE HIM FROM A POSSIBLE HIBER
POPJ P, ; PROBLEM
;MAINT OR START RECEIVED IN THE WRONG MODE
DR.MAI: ;IF EITHER ONE OF THE "WRONG TYPE" OF
DR.STR: PUSHJ P,K.IMPM ; MESSAGES ARRIVES, SAY "IMPROPER MODE"
PJRST DR.PDN ; AND TREAT IT AS "PROTOCOL DOWN"
;DR.IDN ROUTINE TO PROCESS INPUT MESSAGES FROM THE DMR
DR.IDN: DMROFF ;NO INTERRUPTS WHILE HACKING QUEUE
HRRZ T2,DEVDMR(F) ;GET THE ADDRESS OF INPUT MESSAGE QUEUE
JUMPE T2,[HRRM T3,DEVDMR(F) ; IF NO QUEUE, START ONE AND
JRST DR.ID1] ; RETURN
JRST .+2 ;SKIP INTO THE MIDDLE OF THE LOOP
MOVE T2,T1 ;ADVANCE TO THE NEXT ENTRY IN THE LIST
HRRZ T1,MB.NXT(T2) ;GET THE ADDRESS OF THE ENTRY AFTER THAT
JUMPN T1,.-2 ;LOOP IF THE END IS STILL NOT IN SIGHT
HRRM T3,MB.NXT(T2) ;MAKE OUR INPUT MSG BE THE LAST ONE
DR.ID1: DMRON ;RE-ENABLE INTERRUPTS
PUSHJ P,DMRIAV ;SIGNAL THAT INPUT IS AVAILABLE
POPJ P, ; AND RETURN
;DR.ODN DR.OND ROUTINES TO PROCESS RETURNED OUTPUT MESSAGES
DR.ODN:
DR.OND: MOVE T1,T3 ;GET THE ADDRESS OF THE SPENT MSG
PUSHJ P,GIVDRB ; AND FREE IT
SOS DEVOBC(F) ; DECREMENT THE COUNT OF OUTSTANDING MSGS
PJRST DMROAV ; AND WAKE UP THE DRIVER
;DR.RQI ROUTINE TO PROCESS A KONTROLLER'S REQUEST FOR AN INPUT BUFFER
;CALL T3 := NUMBER OF BYTES REQUESTED
;RETURN T1 := 0 IF NO BUFFER AVAILABLE, T1 := ADDRESS OF BUFFER OTHERWISE
DR.RQI: MOVE T2,DEVIOS(F) ;GET DEVICE STATUS
TRNE T2,IOIMPM ;IN WRONG PROTOCOL?
JRST DR.RQ1 ;YES, DON'T ACCEPT MESSAGE
DMROFF ;LOCK THE INPUT CHAIN WHILE WE COUNT BUFFERS
HRRZ T2,DEVDMR(F) ;GET THE ADDRESS OF THE FIRST MESSAGE
MOVEI T1,DMRMXQ ; AND GET THE QUOTA OF INPUT BUFFERS
JUMPN T2,[HRRZ T2,MB.NXT(T2) ;KEEP LOOKING FOR THE END
SOJN T1,. ; AND COUNTING DOWN THE QUOTA
DMRON ; IF THE QUOTA IS EXHAUSTED
POPJ P,] ; THEN RETURN (T1 := 0)
DMRON ;THE CHAIN IS CONSISTANT AGAIN
MOVE T1,T3 ;GET THE BYTE COUNT
PUSHJ P,GETDRB ; AND ALLOCATE A MESSAGE BUFFER
DR.RQ1: SETZ T1, ;IF NONE, TELL THE KONTROLLER
POPJ P, ;RETURN
;ROUTINES TO GET AND FREE DMR BUFFERS.
;FORMAT OF THE BUFFERS IS:
; 0 MB.NXT (POINTER TO NEXT MESSAGE BLOCK)
; 1 MB.FMS (POINTS TO SEGMENT DESCRIPTOR)
; 2 MB.MSN (DDCMP MSG NUMBER)
; 3 CONTAINS SIXBIT /DMRRMD/
; 4 \
; 5 \ MESSAGE SEGMENT
; 6 / DESCRIPTOR GOES HERE
; 7 /
; 8 \
; . } DATA
; N-1 /
; N CONTAINS SIXBIT /RMDDMR/ (CHECK WORD)
DMRHDL==4+MD.LEN ;LENGTH OF DMR HEADER IN WORDS
DMRMSD==4 ;OFFSET TO MSD FROM BEG OF BLK
GETDRB::PUSH P,T1 ;SAVE BYTE COUNT
MOVEI T2,<DMRHDL*4>+3+4(T1) ;ALLOW FOR OVERHEAD AND ROUND UP
LSH T2,-2 ;CONVERT TO WORD COUNT
PUSHJ P,GETWDS## ; AND TRY TO ALLOCATE SPACE
JRST TPOPJ## ;IF NO CORE, GIVE ERROR RETURN
POP P,T2 ;GET BYTE COUNT BACK
SETZM MB.NXT(T1) ;NO POINTERS PLEASE
SETZM DMRMSD+MD.NXT(T1) ;MAKE SURE THERE IS ONLY 1 MSD
MOVEM T2,DMRMSD+MD.BYT(T1) ; AND STORE THAT FOR USER
MOVEM T2,DMRMSD+MD.ALA(T1) ; & PUT IT IN "ALLOC INFO" AREA
MOVEM T2,DMRMSD+MD.ALL(T1) ; ...
MOVSI T3,(POINT 8) ;MAKE A BYTE POINTER TO DATA
HRRI T3,DMRMSD+MD.LEN(T1) ; AREA AFTER HEADER
MOVEM T3,DMRMSD+MD.PTR(T1) ; AND STORE THAT
MOVEM T3,DMRMSD+MD.AUX(T1) ;STORE OUTPUT BYTE POINTER ALSO
MOVEI T3,DMRMSD(T1) ;GET ADDRESS OF MSD
MOVEM T3,MB.FMS(T1) ; AND STORE IT IN MESSAGE BLOCK
MOVEI T2,<DMRHDL*4>+3+4(T2) ;GET BYTE COUNT AGAIN
LSH T2,-2 ; AND CONVERT TO LENGTH OF BLOCK
ADD T2,T1 ;RELOCATE BY ADDRESS OF BLOCK
MOVE T3,[SIXBIT /DMRRMD/] ;GET CHECK DATA
MOVEM T3,3(T1) ;STORE THAT IN FIRST PART
MOVSM T3,-1(T2) ; AND SWAP OF IT IN LAST CHECK WD
RETSKP ;RETURN
;ROUTINE TO RETURN MEMORY
;CALL T1 := ADDRESS OF THE DMR BUFFER
;RETURN CPOPJ ;ALWAYS
GIVDRB::MOVE T2,DMRMSD+MD.ALL(T1) ;RECOVER ORIGINAL BYTE COUNT
MOVEI T2,<DMRHDL*4>+3+4(T2) ;GET LENGTH (IN BYTES)
LSH T2,-2 ; AND CONVERT THAT TO WORDS
MOVE T3,T2 ;COPY LENGTH
ADD T3,T1 ; AND RELOCATE BY BLOCK ADDRESS
MOVE T4,[SIXBIT /DMRRMD/] ;GET CHECK WORD
CAME T4,3(T1) ;CHECK FIRST ONE
PUSHJ P,NTDSTP## ;CHECK WORD CLOBBERED
MOVS T4,T4 ;GET VALUE FOR LAST CHECK WORD
CAME T4,-1(T3) ; AND COMPARE THAT
PUSHJ P,NTDSTP## ;CHECK WORD CLOBBERED
EXCH T1,T2 ;IDIOTIC AC CONVENTIONS
PUSHJ P,GIVWDS## ;RETURN STORAGE
POPJ P, ; AND WE'RE DONE
;THESE ARE THE BYTE POINTERS INTO THE VARIOUS FIELDS OF THE
;DEVICE NAME (USED BY TSTDMR)
DRNDMR: POINT 6,P1,18+6-1 ;KONTROLLER NUMBER
DRSEND::PRGEND
TITLE DMRINT - SERVICE FOR DMR11 V002
SUBTTL T. LITT 21 NOV 81
;From D8KINT V026
SEARCH F, S ,NETPRM, D36PAR
$RELOC
$HIGH
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1981,1983 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VDMRINT,002
DMRINT::ENTRY DMRINT
PURGE NETOFF,NETON
COMMENT @
EHPL MI RTPADE NIISED A DP1P1
END OF COMMENT @
Comment @ DMRINT Functional Description
DMRINT is the actual physical DMR driver. It interfaces to the rest of the
monitor thru the standard DECnet ROUTER/DLL interface. Other users, such as
ANF, UUOCON, or even IBMCOMM translate their private protocols to ours thru
the interface modules (D8RINT, DMRSER, D6RINT).
Requests from users are vectored thru DMRDSP, and use function codes of
the form KF.???. Actually, DMRDSP (DMADSP, DMODSP, DMIDSP) respectively.
Responses from DMRINT to the user are vectored thru the CALUSR routine,
and use function codes of the form KI.???.
Two state variables are important; the line's protocol status (PDRSTS), and
the line's user code (DMRUSR). Because the DMR won't tell us when a line
comes up unless we give it something to do, there are a couple of startup
states where we accept one message to xmit, then continuously return
Output-not-done to further requests until we get an interrupt that implies
that the line came up. DMRUSR tells us who is using the line.
One more kludge; to avoid copying ANF messages into DECnet format buffers,
there is a kludge (not unique to DMR service) we will accept either DECnet
style message blocks (MB????) or ANF style PCB/STC blocks. We determine
the difference by noting that MB.FMS is zero in the ANF format. ANF style
compression is also dealt with here, simply because D8Kxxx chose to.
End comment @
SUBTTL DMRPRM -- PARAMETERS FOR THE DMR
;Define use of the DMR communications region (DMRPPL pages)
;By convention, F will point to the first word of the comm region
;-----------------------------------------------;\
;TBASAD ;DMR base table (128 bytes), used by HW |waste ; \
;-----------------------------------------------; \
;TBFIN0 ;DR%NBF Recieve buffers (EBFSIZ bytes) |waste ; \
;-----------------------------------------------; \
;TBFOU0 ;DR%NBF Transmit buffers(EBFSIZ bytes) |waste ; COMSIZ words
;-----------------------------------------------; allocated on a
;\ page boundary in
; \COMMON.
;Symbols beginning with T are in units of PDP-10 words
;Symbols beginning with E are in units of PDP-11 bytes
;To make life easy, make all 11 addresses on a 10 word boundary
;The code implicitly counts on this, thus the "waste"
;Inputs:
; NETPRM - DMRPPL Number of pages to allocate for comm region
; NETPRM - DR%NBF Number of buffers to allocate per channel
COMSIZ==DMRPPL*1000 ;10 Words in com region
TBASAD==0 ;10 Address of Base Table
EBASAD==0 ;11 Address of Base Table
TBFIN0==TBASAD+<^D128+3>/4 ;10 Address of Input Buffer 0
EBFIN0==TBFIN0*4 ;11 Address of Input Buffer 0
EBFSIZ==<<COMSIZ*4-EBFIN0>/<4*DR%NBF*2>>*4 ;Bytes/buffer
;Bytes/reg - base-tbl-siz / #buf/chn*#chn rounded to 10 wd /4*4
TBFSIZ==EBFSIZ/4 ;10 Words/buffer
IFN <TBFSIZ-DMRMMS>,<Printx ?DMRMMS wrong, update NETPRM>
EBFOU0==EBFIN0+<DR%NBF*EBFSIZ> ;11 Address of Output Buffer 0
TBFOU0==TBFIN0+<DR%NBF*TBFSIZ> ;10 Address of Output Buffer 0
;The following symbols are for information/debugging only
;The code should work for N page comm regions, and M buffers
EBFIN1==EBFIN0+EBFSIZ ;11 Address of Input Buffer 1
TBFIN1==TBFIN0+TBFSIZ ;10 Address of Input Buffer 1
EBFOU1==EBFOU0+EBFSIZ ;11 Address of Output Buffer 1
TBFOU1==TBFOU0+TBFSIZ ;10 Address of Output Buffer 1
;Compute wasted space (Due to rounding (word boundary))
WASTE==COMSIZ-<TBFOU0+<DR%NBF*TBFSIZ>> ;10 Words wasted at end of Comm region
IFL WASTE,<Printx ? DMR11 Communications Region too BIG!>
;Compute total wasted space
WASTE==COMSIZ*4-<^D128 + <2*DR%NBF*EBFSIZ>> ;Total wasted bytes
SUBTTL DEFINITIONS -- DMR11
;DMR-11 BIT DEFINITIONS
DMRSL0==0 ;SEL0
DMRBS0==0 ;BSEL0
DMRTBI==0 ;TRANSMIT BUFFER IN COMMAND
DMRCTI==1 ;CONTROL IN COMMAND
DMRHLT==2 ;HALT PROTOCOL COMMAND
DMRBSI==3 ;BASE IN COMMAND
DMRRBI==4 ;RECEIVE BUFFER IN COMMAND
DMRRQI==40 ;REQUEST INPUT PERMISSION
DMRIEI==100 ;INTERRUPT A ENABLE ON RDI
DMRRDI==200 ;DMR IS READY TO INPUT COMMAND
DMRSSU==400 ;STEP DMR 1 MICROINSTRUCTION
DMRRMI==1000 ;ROM IN - DMR WILL XCT NEXT INSTR FROM SEL6
DMRRMO==2000 ;ROM OUT - DMR WILL PRESENT NEXT CROM LOC OR
; MICROINSTR IN SEL6
DMRLUL==4000 ;TTL LOOP LINE UNIT SERIAL DATA
DMRSLU==10000 ;STEP LINE UNIT 0 RCV SHIFT 1 XMT SHIFT
DMRIMD==20000 ;INHIBIT MICRODIAGNOSTICS (COMPLEMENT SWITCH)
DMRMRC==40000 ;MASTER CLEAR DMR (SELF-CLEARING)
DMRRUN==100000 ;RUN
DMRBS1==1 ;BSEL1
DMRSL2==2 ;SEL2
DMRBS2==2 ;BSEL2
DMRTBO==0 ;TRANSMIT BUFFER OUT
DMRRBO==4 ;RECEIVE BUFFER OUT
DMRCTO==1 ;CONTROL OUT
DMRIEO==100 ;INTERRUPT ENABLE ON RDO
DMRRDO==200 ;DMR HAS OUTPUT A NEW COMMAND
DMRBS3==3 ;BSEL3
DMRMTF==1 ;MICROPROCESSOR BOARD TEST FAILED
DMRLTF==2 ;LINE UNIT BOARD TEST FAILED
DMRMDI==100 ;MICRODIAGNOSTICS WERE INHIBITED
DMRMDR==200 ;MICRODIAGNOSTICS RAN
DMRSL4==4 ;SEL4
DMRBS4==4 ;BSEL4
;MODEM STATUS BITS -- VALID WHEN RDI HAS SET
DMRMCD==1 ;MODEM CARRIER DETECT/RCVR ACTIVE
DMRMSB==2 ;RS449 STANBY LEAD
DMRMCS==4 ;CLEAR TO SEND
DMRMSR==10 ;DATA SET READY
DMRMHD==20 ;LINE UNIT IN HDX MODE
DMRMRS==40 ;REQUEST TO SEND
DMRMTR==100 ;DATA TERMINAL READY
DMRMRG==200 ;RING
DMRMTM==2000 ;MODEM IN TEST MODE
DMRMSQ==40000 ;SIGNAL QUALITY (CARRIER DETECT LOOKALIKE)
DMRMSR==100000 ;SIGNAL RATE
DMRBS5==5 ;BSEL5
DMRSL6==6 ;SEL6
DMRBS6==6 ;BSEL6
;MORE MODEM STATUS BITS
DMRMRH==1 ;RTS HOLD - IN FDX, LT 1MBAUD DMR HOLDS RTS WHEN
; LINE IDLE, EXCEPT ERROR RECOVERY
DMRMHX==20 ;LINE UNIT IN HDX MODE
DMRMTX==100 ;DATA TERMINAL READY
;CONTROL-OUT STATUS BITS
DMRONK==1 ;NAK THRESHOLD EXCEEDED
DMROTO==2 ;REP TIMEOUT (7 CONSEC REPS XMITTED)
DMRONB==4 ;7 NAKS RECEIVED DUE TO NO BUFFER
DMROMM==10 ;MAINT MESSAGE RECEIVED (FATAL)
DMROLM==20 ;MESSAGE LONGER THAN BUFFER RECEIVED (FATAL)
DMROHU==100 ;DSR DROPPED (OTHER END HUNG UP)
DMROSR==200 ;START RECEIVED WHILE RUNNING (FATAL)
DMRONX==400 ;NXM WHILE XMT/RCV/BASE TABLE UPDATE (FATAL)
DMROHC==1000 ;HALT COMPLETE
;CONTROL-IN COMMAND BITS
DMRILS==4000 ;Use long start timer
DMRIHD==2000 ;Use half duplex mode
DMRIMT==400 ;Use DDCMP maintenance mode
DMRBS7==7 ;BSEL7
;MODEM STATUS BITS - VALID ONLY AFTER BASE TABLE ASSIGNED
DMRMPS==1 ;PROGRAM SELECTED - MAINT BIT
DMRMIM==10 ;INTEGRAL MODEM SELECTED
DMRM35==20 ;V.35 MODEM SELECTED
DMRM32==100 ;RS232-C OR RS423-A MODEM SELECTED
DMRM22==200 ;RS422-A MODEM SELECTED
SUBTTL DMRONC -- ONCE ONLY ROUTINE FOR DMR
;THIS ROUTINE IS CALLED BY SYSINI. IT VERIFYS THAT ALL DMR11
;UNITS SPECIFIED BY MONGEN ACTUALLY EXIST.
DMRONC::PUSHJ P,SAVE4## ;HERE FROM ONCE. WE USE 4 P'S
SETZ P1, ;P1 IS CURRENT DMR INDEX, START WITH DMR ZERO
DMROCP: CAIL P1,M.DMRN## ;LOOP OVER ALL DMRs
POPJ P, ;IF WE'VE DONE THEM ALL, RETURN
SKIPN W,DMRTBL##(P1) ;GET THE ADDRESS OF THE NEXT DMR BLOCK
AOJA P1,DMROCP ; IF IT'S NOT THERE, TRY THE NEXT ONE
MOVE T1,DMRCSR(W) ;GET THE ADDRESS OF THE DMR11
PUSHJ P,UBGOOD## ;SEE IF IT EXISTS (UNIBUS TRAP/DOESN'T TRAP)
JRST DMRNXP ;NON-EXISTANT DMR
PUSHJ P,DMRINI ;Restart the DMR(In it's MONGEN'd mode)
JFCL ;If we lost, we lost
AOJA P1,DMROCP ;Try for next
;DMRNXP - ROUTINE TO DECLARE A DMR11 NON-EXISTANT. UNIT IS MARKED
; NON-EXISTANT BY PUTTING A ZERO IN IT'S "DMRTBL" ENTRY.
DMRNXP: SETZM DMRTBL##(P1) ;CLEAR THE DMR TABLE ENTRY SO IT'S IGNORED
MOVE U,OPRLDB## ;GET THE ADDRESS OF THE OPR'S TTY
PUSHJ P,INLMES## ;TELL HIM FIRST PART OF BAD NEWS.
ASCIZ /
? Can't find /
PUSHJ P,PRDMR ;TELL HIM WHICH DMR (FROM "W")
PUSHJ P,INLMES## ;FINISH THE MESSAGE
ASCIZ /.
/
AOJA P1,DMROCP ;GO DO NEXT DMR
PDRSTS::POINT 3,DMRSTS(W),2 ;Pointer to state byte in status word
SUBTTL DMRSEC -- ONCE/SECOND ROUTINE FOR DMR
;THIS ROUTINE IS CALLED BY CLOCK1. IT CHECKS ALL DMRS FOR RECEIVE
;BUFFER STARVATION, AND IF STARVED, TRYS TO DELIVER MESSAGE AGAIN.
DMRSEC::PUSHJ P,SAVE4## ;HERE FROM CLOCK1. WE USE 4 P'S
PUSHJ P,SAVEFW ;SAVE F & W AS WELL
SETZ P1, ;P1 IS CURRENT DMR INDEX, START WITH DMR ZERO
DMRSC0: CAIL P1,M.DMRN## ;LOOP OVER ALL DMRs
POPJ P, ;IF WE'VE DONE THEM ALL, RETURN
SKIPN W,DMRTBL##(P1) ;GET THE ADDRESS OF THE NEXT DMR BLOCK
AOJA P1,DMRSC0 ; IF IT'S NOT THERE, TRY THE NEXT ONE
MOVE F,DMRTAD(W) ;GET ADDRESS OF COMM PAGE
AOS DMRZTM(W) ;Another second of uptime
PUSHJ P,FREMAI ;If in maint mode, free xmitted msgs
MOVEI T1,DMRSTV ;Get the starvation bit
LDB T2,PDRSTS ;Get current state
CAIL T2,DR%MAI ;If not at least in maint mode, or
TDNN T1,DMRSTS(W) ;If this one isn't starving
AOJA P1,DMRSC0 ;Leave the poor thing alone
DMROFF ;Prevent races
;Can the fact that we check the starvation bit in RCVBUF
;prevent us from ever having all the buffers out?
;Perhaps above test should be removed...
;Or starvation should be a counter??
SKIPL T1,DMRRBC(W) ;Get count of receive buffers DMR has
CAILE T1,DR%NBF ;Too big?
PUSHJ P,NTDSTP## ;++ Ridiculous outstanding RBF count
CAIGE T1,DR%NBF ;Less than max?
PUSHJ P,RCVMSG ;Pass buffer to USER
DMRON ;Allow interrupts again
;Note that we don't try for many msgs
;on the theory that we were recently core poor
AOJA P1,DMRSC0 ;Try for next
SUBTTL DMRDSP - ENTRY TO DMRINT
;DMRDSP - THIS ROUTINE IS DECnet'S ENTRY INTO DMRINT.
;CALL MOVX T1,FUNCTION-CODE (KF.???)
; MOVX T2,DMR-BLOCK ADDRESS
; MOVX T3,BUFFER ADDRESS OR PARAMETER # (ONLY FOR KF.QOB/NTMAN)
; MOVX T4,PARAMETERVALUE (ONLY KF.SET)
; PUSHJ P,DMRDSP/DMADSP/DMODSP/DMIDSP
;RETURN CPOPJ ;WHEN WE ARE CALLED BY THE WRONG USER
; CPOPJ1 ;ON SUCCESS
;For DECnet
DMRDSP::SKIPL T1 ;FIRST RANGE CHECK THE
CAILE T1,KF.CLR ; FUNCTION CODE [KF.MAX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR
MOVE W,T2 ;Point to the DMR block
HRRZ T2,DMRUSR(W) ;Get the line user
CAIE T2,DD.DEC ;If not DECnet,
POPJ P, ;Die -- shouldn't use DMRDSP!
MOVE F,DMRTAD(W) ;Get address of Comm region
PUSHJ P,@DMRDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;ORDINARY RETURN
PJRST CPOPJ1## ;AND GIVE THE GOOD RETURN
;For ANF-10
DMADSP:SKIPL T1 ;FIRST RANGE CHECK THE
CAILE T1,KF.QOB ; FUNCTION CODE [KF.MAX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR
MOVE W,T2 ;Point to the DMR block
HRRZ T2,DMRUSR(W) ;Get the line user
CAIE T2,DD.ANF ;If not ANF-10,
POPJ P, ;Die -- shouldn't use DMRDSP!
MOVE F,DMRTAD(W) ;Get address of Comm region
PUSHJ P,@DMRDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;ORDINARY RETURN
PJRST CPOPJ1## ;AND GIVE THE GOOD RETURN
;For Program mode
DMODSP:SKIPL T1 ;FIRST RANGE CHECK THE
CAILE T1,KF.QOB ; FUNCTION CODE [KF.MAX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR
MOVE W,T2 ;Point to the DMR block
HRRZ T2,DMRUSR(W) ;Get the line user
CAIE T2,DD.PRO ;If not Program mode
POPJ P, ;Die -- shouldn't use DMRDSP!
MOVE F,DMRTAD(W) ;Get address of Comm region
PUSHJ P,@DMRDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;ORDINARY RETURN
PJRST CPOPJ1## ;AND GIVE THE GOOD RETURN
;For IBMcomm
DMIDSP::SKIPL T1 ;FIRST RANGE CHECK THE
CAILE T1,KF.QOB ;FUNCTION CODE [KF.MAX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR
MOVE W,T2 ;Point to the DMR block
HRRZ T2,DMRUSR(W) ;Get the line user
CAIE T2,DD.IBM ;If not IBMcomm,
POPJ P, ;Die -- shouldn't use DMRDSP!
MOVE F,DMRTAD(W) ;Get address of Comm region
PUSHJ P,@DMRDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;ORDINARY RETURN
PJRST CPOPJ1## ;AND GIVE THE GOOD RETURN
DMRDST: IFIW KF.HA ;0 = HALT ALL PROTOCOLS
IFIW KF.IN ;1 = INITIALIZE NORMAL PROTOCOL (DDCMP)
IFIW KF.MA ;2 = INITIALIZE MAINT PROTOCOL (BOOTING)
IFIW KF.QO ;3 = QUEUE OUTPUT BUFFERS (MAINT OR NORMAL)
IFIW KF.SE ;4 = SET \
IFIW KF.RE ;5 = READ ) NETWORK MANAGEMENT
IFIW KF.CL ;6 = CLEAR/
;KF.HA - ROUTINE TO SHUT-DOWN PROTOCOL ON A DMR-LINE.
;CALL MOVX W,DMR-BLOCK ADDRESS
; PUSHJ P,KF.HA ;FROM EITHER CLOCK OR UUO LEVEL WITH THE
; ; INTERRUPTS ON.
;RETURN CPOPJ1 ;ALWAYS - HALTS DMR IF ERROR
KF.HA: LDB T1,PDRSTS ;FIRST GET OUR STATE,
CAIGE T1,DR%FLS ;IF WE ARE ALREADY "HALTED"
RETSKP ;RETURN WITH OUT TOUCHING ANYTHING
PUSHJ P,DMRKL0 ;SILENTLY KILL THE DMR
PUSHJ P,DMRPDN ;TELL OUR DRIVER THAT WE ARE DOWN
MOVEI T1,DR%HLT ;NOW GO
DPB T1,PDRSTS ;TO "INITIALIZED" STATE
RETSKP ;AND THE "HALT" COMMAND IS DONE
;KF.IN - ROUTINE TO INITIALIZE THE DMR AND PUT IT IN NORMAL PROTOCOL
;CALL MOVX W,DMR-BLOCK ADDRESS
; PUSHJ P,KF.IN
;RETURN CPOPJ1 ;ALWAYS
KF.IN: PUSHJ P,DMRPD0 ;CLEAN OUT QUEUES
MOVEI T1,DMRSMT ;The start in maintenance mode bit
ANDCAM T1,DMRSTS(W) ;We don't wanna
PUSHJ P,DMRINI ;Crank it up
JFCL
RETSKP ;AND WE'RE DONE
;KF.MA - ROUTINE TO INITIALIZE MAINT PROTOCOL ON THE DMR-LINE
;CALL MOVX W,DMR-BLOCK
; PUSHJ P,KF.MA
;RETURN CPOPJ1 ;ALWAYS
KF.MA: LDB T1,PDRSTS ;FIRST MAKE GET OUR STATE, AND
CAIN T1,DR%MAI ; IF WE ARE ALREADY IN MAINT STATE,
RETSKP ; THEN DON'T DO ANYTHING MORE
CAIL T1,DR%WT1 ;IF WE ARE RUNNING (OR TRYING TO START)
PUSHJ P,KF.HA ; THEN FLUSH ALL BUFFERS
JFCL ;IGNORE ERROR RETURN
MOVEI T1,DR%MAI ;GET AND SET THIS LINE TO
DPB T1,PDRSTS ; MAINT STATE
MOVEI T1,DMRSMT ;Start in maint bit
IORM T1,DMRSTS(W) ;Insist on it
PUSHJ P,DMRINI ;Crank it up again
JFCL
RETSKP ;AND WE'RE DONE
;KF.QO - ROUTINE TO QUEUE OUTPUT MESSAGES
;CALL MOVX W,DMR-BLOCK ADDRESS
; MOVX T3,MESSAGE BUFFER ADDRESS
; PUSHJ P,KF.QO
;RETURN CPOPJ1 ;ALWAYS
KF.QO: HLLZS MB.NXT(T3) ;ZERO THE FORWARD POINTER
SETOM MB.MSN(T3) ;-1 IN NUMBER FIELD MEANS NO NUMBER ASSIGNED
DMROFF ;PREPARE TO MESS WITH THE QUEUES
LDB T1,PDRSTS ;GET STATE
CAIE T1,DR%RUN ;ARE WE IN STILL RUN STATE?
CAIN T1,DR%MAI ;IT'S OK TO SEND DATA IN MAINT MODE TOO
JRST KF.QO0 ;GOODNESS, ITS OK
CAIN T1,DR%WT1 ;If in this state,
JRST [MOVEI T1,DR%WTD ;Allow this, but reject following
DPB T1,PDRSTS ;By advancing a state
JRST KF.QO0]
DMRON ;NO, PASS PTR TO MSG IN T3
MOVEI T1,KI.OND ; GET THE "OUTPUT NOT DONE" FUNCTION
PUSHJ P,CALUSR ; AND TELL OUR DRIVER THE NEWS
RETSKP ;This is still "success"
KF.QO0: HRRZ T1,DMRWTO(W) ;GET THE FIRST MESSAGE ON THE "WAIT OUTPUT" Q
JUMPE T1,[HRRZM T3,DMRWTO(W) ;IF NONE, THEN WE'RE FIRST
JRST KF.QO2] ;GO TRY TO SEND IT
KF.QO1: HRRZ T2,MB.NXT(T1) ;GET THE ADDRESS OF THE NEXT MESSAGE
JUMPE T2,[HRRM T3,MB.NXT(T1) ;IF NONE, APPEND TO THE END
JRST KF.QO2] ;AND FIRE UP THE XMITTER
MOVE T1,T2 ;STEP TO NEXT MSG IN QUEUE
JRST KF.QO1 ;AND SEE IF IT'S THE LAST
KF.QO2: PUSHJ P,RCVBUF ;MAKE SURE A RCV BUFFER IS QUEUED
PUSHJ P,XMTBUF ;TRY TO SEND THE MESSAGE
DMRON ;DONE MESSING
RETSKP ;DONE
Subttl NTMAN -- Interface to NTMAN for DECnet
;KF.SE - SET FUNCTIONS FOR NTMAN
;
KF.SE: JUMPN T3,KF.SE1 ;IS THIS PARAMETER 0 (STATE?)
CAIE T4,NCK.ON ;ARE WE SETTING STATE ON?
JRST KF.S01 ;NO
LDB T1,PDRSTS ;GET CURRENT STATE
CAIE T1,DR%RUN ;IS IT CURRENTLY RUNNING
CAIN T1,DR%MAI ;OR IN MAINTENANCE STATE?
JRST KFERRR ;WE SHOULDN'T BE DOING THIS
PJRST KF.IN ;INITIALIZE THE LINE
KF.S01: CAIE T4,NCK.OF ;ARE WE SETTING STATE OFF?
JRST KF.S02 ;NOPE
PJRST KF.HA ;HALT THE LINE
KF.S02: ;CAIE T4,NCK.SR ;ARE WE SETTING STATE TO SERVICE?
; JFCL ;NO
JRST KFERRR ;YES, I DON'T KNOW WHAT TO DO ABOUT IT.
KF.SE1: CAIE T3,^D1111 ;DUPLEX?
JRST KF.SE2 ;NO
LDB T1,PDRSTS ;GET LINE STATE
CAIE T1,DR%HLT ;HALTED?
JRST [MOVNI T1,^D11 ;NO, IN WRONG STATE
POPJ P,] ;DIE
MOVEI T1,DMRSHD ;HALF DUPLEX BIT
TDNN T1,DMRSTS(W) ;SET?
TDZA T2,T2 ;NO
MOVEI T2,1 ;YES, HALF NOW
CAIE T2,(T4) ;ALREADY CORRECT?
XORM T1,DMRSTS(W) ;NO, RESET TO NEW STATE
RETSKP ;SUCCESS
KF.SE2: ;CHECK FOR OTHER PARAMETERS HERE
KFERRR: SETO T1, ;INDICATE ERROR
POPJ P, ;AND RETURN
KFNULL: SETZ T1, ;INDICATE NO ERROR
POPJ P, ;AND RETURN (TO SET NXNIL IN NTMAN)
;KF.RE - READ PARAMETERS FOR NETWORK MANAGEMENT
KF.RE: JUMPN T3,KF.RE1 ;IF NOT READING STATE, PROCEED
LDB T1,PDRSTS ;GET DMR STATE
MOVE T1,[NCK.OF ;DR%HLT : OFF
NCK.OF ;DR%FLS : OFF -- HALTING
NCK.ON ;DR%MAI : ON -- MAINT
NCK.ON ;DR%WT1 : ON -- STARTING
NCK.ON ;DR%WTD : ON -- SYNCHRONIZING
NCK.ON](T1) ;DR%RUN : ON
RETSKP ;AND RETURN WITH STATE
KF.RE1: CAIE T3,1 ;SUBSTATE?
JRST KF.RE2 ;NO, PROCEED
LDB T1,PDRSTS ;GET DMR STATE
SKIPL T1,[EXP -1 ;DR%HLT : OFF
EXP -1 ;DR%FLS : OFF -- HALTING
EXP -1 ;DR%MAI : SERVICE
NCB.ST ;DR%WT1 : ON -- STARTING
NCB.SN ;DR%WTD : ON -- SYNCHRONIZING
EXP -1](T1) ;DR%RUN : ON
RETSKP ;WE GOT A SUBSTATE, RETURN IT
SETZ T1, ;NOTHING, INDICATE NXNIL
POPJ P,
KF.RE2: CAIE T3,^D1112 ;IS THIS ASKING FOR PROTOCOL?
JRST KF.RE3 ;NO
SETZ T1, ;VALUE OF ZERO IS DDCMP POINT TO POINT
RETSKP
KF.RE3: CAIE T3,^D1111 ;ASKING FOR DUPLEX?
JRST KFNULL ;NO
MOVEI T1,DMRSHD ;HALF DUPLEX BIT
TDNN T1,DMRSTS(W) ;SET?
TDZA T1,T1 ;NO, FULL
MOVEI T1,1 ;YES, HALF
RETSKP ;THUS
;KF.CL - Clear parameters
KF.CL: JRST KFERRR ;DO NOTHING ABOUT THIS JUST YET
SUBTTL INTERRUPTS -- INTERRUPT LEVEL INTERFACE TO THE DMR11
Comment @
Each DMR11 has two interrupt vector addresses.
"A" This interrupt is taken when RDYI (DMRRDI) comes up. In this
state the DMR11 is ready for an input transaction. All
input transactions for the DMR11 are queued in the DMR block.
This is necessary because the following situation would otherwise
cause a deadlock.
1) The DMR11 sets RDYO and gives a BUFFER-OUT transaction.
2) At interrupt level, we want to do a BUFFER-IN.
3) If, in the meantime, the DMR11 has set RDYO again,
we will not be able to get RDYI until we process another
output transaction.
The solution to this is to queue all input transactions. This does
mean that we have to take an interrupt on each transaction, but it
does circumvent the problem.
"B" This interrupt is taken when RDYO (DMRRDO) comes up. In this
state the DMR11 is wants to perform an output transaction.
It is these output transactions that drive almost all of the
interrupt level DMR processing.
The vector instructions are set up to be JSR's to the locations "DMRIVA",
and "DMRIVB" in the DMR block for the DMR11. These locations contain the
10 instructions necessary to save the AC's, load "W" with the address
of the particular DMR block, load "F" with the address of the comm region and
dispatch to either of the two interrupt routines "DMRAIV" or "DMRBIV".
End comment @
SUBTTL DMRAIV -- DMR11 INTERRUPT VECTOR "A" PROCESSING.
;DMRAIV -- ROUTINE TO HANDLE DMR11 INTERRUPT VECTOR "A" (INPUT)
;CALL MOVE W,[EXP DMR-BLOCK-ADDRESS]
; MOVE F,[EXP DMR-COMM-REGION-ADDRESS]
; PUSHJ P,DMRAIV ;CALLED FROM DMRIVA IN THE DMR BLOCK
;RETURN POPJ P, ;TO DISMISS THE INTERRUPT.
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;ON MULTI-PROCESSOR SYSTEMS, THIS CODE WILL NEED TO AOSE THE DMR INTERLOCK
;
DMRAIV::AOS DMRACT(W) ;COUNT THE INTERRUPT
LDB T1,PDRSTS ;Get line status
CAIN T1,DR%HLT ;If we're stopped
PJSP T1,DMRERR ; CLEAR "RUN" SO INTS WILL STOP
MOVE U,DMRCSR(W) ;GET THE UNIBUS ADDRESS OF THE DMR11
MOVEI T1,DMRRDI ;GET THE "RDYI" FLAG
TION T1,DMRSL0(U) ;MAKE SURE "RDYI" IS UP
PJSP T1,DMRERR ; IF IT'S NOT, THEN ITS AN ILLEGAL INTERRUPT
RDIO T1,DMRSL4(U) ;Read low-order modem status bits
RDIO T2,DMRSL6(U) ;Read high-order bits
HRL T2,T1 ;Low word,,high word
MOVEM T2,DMRMST(W) ;Save last known status
MOVE T3,DMRIQT(W) ;GET NUMBER OF NEXT QUEUED TRANSACTION
CAMN T3,DMRIQP(W) ;MAKE SURE THAT IT'S DIFFERENT NOT THE "PUTTER"
PJSP T1,DMRERR ; IF IT IS, THEN WE'RE GETTING UNSOLICITED
; INTERRUPTS. DECLARE DMR ILL.
ASH T3,1 ;MAKE IT AN OFFSET INTO THE QUEUE
ADDI T3,DMRINQ(W) ;RELOCATE BY THE ADDRESS OF THE QUEUE
MOVE T1,0(T3) ;GET SEL0 DATA
ANDI T1,17 ;Only command code
MOVE T2,1(T3) ;GET SEL4, SEL6 DATA
AOS T3,DMRIQT(W) ;ADVANCE QUEUE TAKER
CAIL T3,DMRIQN ;IF ITS TIME TO WRAP AROUND, THEN
SETZB T3,DMRIQT(W) ; WRAP AROUND TO THE FIRST ENTRY
WRIO T2,DMRSL6(U) ;STORE TOP WORD
MOVSS T2,T2 ;GET SEL4 DATA
WRIO T2,DMRSL4(U) ; AND STORE THAT
MOVEI T2,17 ;COMMAND CODE FIELD
BCIO T2,DMRSL0(U) ;CLEAR IT OF OLD VALUE
BSIO T1,DMRSL0(U) ;SET NEW COMMAND INTO PLACE
MOVEI T1,DMRRQI ;Request bit
BCIO T1,DMRSL0(U) ;Clear it to say command ready
MOVEI T2,DMRRDI ;DMR's response bit
MOVEI T4,2000 ;Wait a while
TIOE T2,DMRSL0(U) ; for DMR to respond
SOJG T4,.-1 ;Not accepted yet, wait a while
SKIPG T4 ;Well...
PJSP T1,DMRERR ;Sigh, timed out - DMR must be ill
CAME T3,DMRIQP(W) ;Is queue empty now?
BSIO T1,DMRSL0(U) ;No, request another interrupt
;Note that we can't wait since DMR may already be doing RDO
POPJ P, ;ALL DONE
SUBTTL DMRBIV -- DMR11 Interrupt vector "B" processing.
;DMRBIV -- ROUTINE TO HANDLE DMR11 INTERRUPT VECTOR "B" (OUTPUT)
;CALL MOVE W,[EXP DMR-BLOCK-ADDRESS]
; MOVE F,[EXP DMR-COMM-REGION-ADDRESS]
; PUSHJ P,DMRBIV ;CALLED FROM DMRIVB IN THE DMR BLOCK
;RETURN POPJ P, ;TO DISMISS THE INTERRUPT
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAY)
;
;ON MULTI-PROCESSOR SYSTEMS THIS CODE WILL NEED TO AOSE THE DMR INTERLOCK.
;
DMRBIV::AOS DMRBCT(W) ;COUNT THE INTERRUPT
LDB T1,PDRSTS ;Get line status
CAIN T1,DR%HLT ;See if we're OK
PJSP T1,DMRERR ; IF WE'RE NOT, CLEAR RUN AND RETURN
MOVE U,DMRCSR(W) ;GET THE UNIBUS ADDRESS OF THE DMR
RDIO P1,DMRSL2(U) ;READ STATUS BITS
TRNN P1,DMRRDO ;BETTER WANT AN OUTPUT TRANSACTION
PJSP T1,DMRERR ;ILLEGAL INTERRUPT. CRASH DMR
RDIO P2,DMRSL4(U) ;Read first data port
RDIO P3,DMRSL6(U) ;Read second data port
MOVEI T1,DMRRDO ;GET THE RDYO BIT
BCIO T1,DMRSL2(U) ;CLEAR IT TO LET THE DMR CONTINUE
ANDI P1,7 ;Get output command code
CAIG P1,4 ;Legal?
XCT [JRST D8RTXC ;(0) Xmit complete (TBA/CCO)
JRST D8RCTO ;(1) Control out (Status from DMR)
JSP T1,DMRERR ;(2) Reserved code
JSP T1,DMRERR ;(3) Reserved code
JRST D8RRCV](P1) ;(4) Receive complete (RBA/CCO)
;ROUTINE DISPATCHED TO WILL RETURN.
JSP T1,DMRERR ;Output command out of range
SUBTTL D8RTXC -- DMR11 TRANSMIT COMPLETE TRANSACTION PROCESSING.
; This routine frees output buffers when a TRANSMIT COMPLETE transaction
;declares that the DMR has output all data in the buffer. It:
; 1) Returns the transmitted (and DDCMP ACK'd) message to NETSER
; 2) Performs all necessary book-keeping
; 3) Tries to fill the freed output buffer
;
;called with:
; F, W := COMM and DMR pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the DMR11
;
D8RTXC: PUSHJ P,GETBAD ;GET T4 SET UP TO POINT TO Buffer
TLNE T4,3 ;MAKE SURE THAT BUFFER-OUT STARTS ON EVEN BYTE
PJSP T1,DMRERR ; IF ODD BYTE, THEN DMR11 SCREWED US
MOVSI T2,(1B0) ;GET THE BUFFER IN-USE MARK BIT
TDNN T2,0(T4) ; AND MAKE SURE THAT IT'S SET
PUSHJ P,NTDSTP## ;WE'VE SCREWED UP THE BUFFERS [DBUG]
ANDCAM T2,0(T4) ;CLEAR THE USE BIT
AOS DMRXMC(W) ;Another message transmitted
SOSGE DMRCTA(W) ;One less buffer awaiting ACK
PJSP T1,DMRERR ;++ COUNT NEGATIVE(or wrong buffer returned).
HRRZ T3,DMRWTA(W) ;Get address of first message out
SKIPN T3 ;Anything?
PJSP T1,DMRERR ;++ TBA/CCO with no message sent
HRRZ T2,MB.NXT(T3) ;Get address of next message waiting ack
HRRZM T2,DMRWTA(W) ;It's now first in q
HLLZS MB.NXT(T3) ;Let's not confuse qs
LDB T2,PDRSTS ;Get current status
CAIN T2,DR%MAI ;If in Maint mode
JRST D8RTXM ;Must defer posting done (NETSER core alloc..)
MOVEI T1,DR%RUN ;Just in case we're starting up
CAIE T2,DR%WT1 ;See if waiting for first msg (SNH!)
CAIN T2,DR%WTD ;More likely, waiting for done from first msg
DPB T1,PDRSTS ;If so, mark the line as really up!
MOVEI T1,KI.ODN ;What we did
PUSHJ P,CALUSR ; -- Tell user it was sent
PUSHJ P,XMTBUF ;NOW GO TRY TO FILL THE JUST FREED BUFFER
POPJ P, ;All set, dismiss interrupt
D8RTXM: AOSG DMRMAC(W) ;Count one more message on queue
HRRZ T2,DMRMAI(W) ;Get pointer to current first message
HRRM T2,MB.NXT(T3) ;String it after this one
HRRZM T3,DMRMAI(W) ;And put this one at the head of the queue
PUSHJ P,XMTBUF ;If possible, give the DMR more to do...
POPJ P, ;Done
SUBTTL D8RRCV -- ROUTINE TO HANDLE RECEIVE COMPLETE TRANSACTIONS.
; This routine handles RECEIVE COMPLETE transactions. These
;transactions consist of input messages coming in over the synchronous
;line. This routine:
; 1) Frees the receive buffer. (No worry about races, this
; code runs under the DMR interlock on multiprocessor
; systems. On a single processor system, it runs at
; interrupt level. Hence no one will try to allocate
; the buffer between when it is freed and when it is
; processed
; Because the DMR may have received a message before NETSER has assigned
; a receive PCB, and because the DMR has already DDCMP ACK'd the message,
; we maintain a queue of ACK'd but unposted messages. When the buffer is
; finally posted, we dequeue the buffer in D8RRDD.
; 2) If no PCB available, queue received buffer
; Else pass buffer to NETSER
;
;called with:
; F, W := COMM and DMR pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the DMR11
;
D8RRCV: PUSHJ P,FREBOI ;FREE THE INPUT BUFFER, (SET UP T3/T4)
PJSP T1,DMRERR ;?? DMR11 GAVE BAD BDL ADDRESS ??
SOS DMRRBC(W) ;One less buffer outstanding
MOVE T1,DMRRBG(W) ;Get GIVE pointer
SUB T1,DMRRBC(W) ;Next expected in(=Last given - # left in DMR)
SKIPGE T1 ;But if time to wrap around,
ADDI T1,DR%NBF ;Do so now
MOVE T2,T1 ;Get a copy of the buffer number
ADDI T1,DMRRCC(W) ;Index into byte count table
MOVEM T3,(T1) ;Save byte count of buffer
IMULI T2,TBFSIZ ;Offset in buffer space
ADDI T2,TBFIN0(F) ;Make 10 CBP (Buffers start on word boundary)
CAME T2,T4 ;Make sure that predicted matches actual
PUSHJ P,NTDSTP## ;++ DMR returned wrong buffer
PJRST RCVMSG ;Now see if we can get a buffer to put it in
SUBTTL D8RCTO -- Routine to handle CONTROL-OUT (status) transactions
;This routine handles CONTROL-OUT (status) transactions. These transactions
;are all errors detected by the DMR. Fatal errors (noted below) halt the
;DMR. Non-fatal errors indicate that something may or may not be wrong
;with the link.
;This code:
; 1) Converts the bit mask returned by the DMR into a dense
; code for counting
; 2) If the error was fatal, restarts the DMR and the line,
; notifys NETSER
; 3) If the error was non-fatal, ignores it for the time being.
; Version 3 should check for a threshold for each error type,
; and restart the line if exceeded.
; For now, DSR drop is considered FATAL only if it happened on a
; switched line. This is to allow for glitches on short haul modems.
; A case can be made that it should always be fatal. But until someone
; convinces me, the crok remains...
;
;Called with:
; F,W := COMM and DMR pointers
; P1,P2,P3 := SEL2, SEL4, and SEL6 at interrupt time
D8RCTO: MOVEI T1,DMRSSW ;Get switched line bit
TDNE T1,DMRSTS(W) ;Is this one?
TRNN P3,DMROHU ;Yes, DSR drop? (Other end hung up)
TRNE P3,DMROMM!DMROLM!DMROSR!DMRONX!DMROHC ;Fatal error?
TLOA P3,400K ;Yes, set composite error
TLZ P3,400K ;No, Unnecessary, but clear anyhow
TRNE P3,DMRONK ;Too many NAKs?
AOS DMRCTT+00(W) ;Yes, count event
TRNE P3,DMROTO ;Too many REPs?
AOS DMRCTT+01(W) ;Yes, count event
TRNE P3,DMRONB ;Too many NO BUFFER NAKs?
AOS DMRCTT+02(W) ;Yes, count event
TRNE P3,DMROMM ;Maint message received?
PUSHJ P,[AOS DMRCTT+03(W) ;Yes
PJRST RCVMAI] ;Go see what to do
TRNE P3,DMROLM ;Received message too long?
AOS DMRCTT+04(W) ;Yes
TRNE P3,DMROHU ;DSR drop (Other end hung up)?
AOS DMRCTT+05(W) ;Yes
TRNE P3,DMROSR ;Start received while running?
JRST [AOS DMRCTT+06(W) ;Yes
JRST RCVSTR] ;Process start received
TRNE P3,DMRONX ;NXM detected by DMR?
AOS DMRCTT+07(W) ;Yes
TRNE P3,DMROHC ;Halt complete?
AOS DMRCTT+10(W) ;Yes
JUMPGE P3,CPOPJ## ;Return if no action required
PJRST DMRKIL ;For now, just restart DMR
SUBTTL RCVMSG -- ROUTINE TO DISPATCH ON THE DDCMP MESSAGE TYPE.
;Called from D8RRCV and D8RRDD with:
; F, W := com and DMR pointers
;
RCVMSG: AOS T4,DMRRBT(W) ;Get index of next buffer from DMR
CAIL T4,DR%NBF ;Too big?
SETZB T4,DMRRBT(W) ;Yes, wrap around
MOVE T3,T4 ;Get a copy of buffer number
ADDI T3,DMRRCC(W) ;Index into count table
MOVE T3,(T3) ;Get saved count
IMULI T4,TBFSIZ ;Offset by 10 size of buffer
ADDI T4,TBFIN0(F) ;Get address of received data
LDB T1,PDRSTS ;Get line status
CAIL T1,DR%WT1 ;Running?
JRST RCVDAT ;Yes, data message
CAIN T1,DR%MAI ;Maintenance mode?
JRST RCVMA1 ;Yes, maintenance message
PJSP T1,DMRERR ;++ Message received but DMR not running!
;Here if can't get core for a received message.
;Deadvance the RCC queue, so we don't skip a message later...
RCVABT: MOVEI T4,DR%NBF-1 ;Get the maximum queue entry
SOSGE DMRRBT(W) ;Back up the queue
MOVEM T4,DMRRBT(W) ;If we went thru zero, wrap properly
POPJ P, ;We'll get this one eventually at DMRSEC
SUBTTL RCVMAI -- ROUTINE TO PROCESS INCOMING MAINTENANCE MESSAGES.
RCVMAI: LDB T1,PDRSTS ;Get line's state
CAIG T1,DR%MAI ;If not in some normal state, leave OPR alone
JRST RCVMA0 ;We were halted or halting
CAIL T1,DR%WT1 ;If user thought we were running
PUSHJ P,DMRPDN ;Tell him protocol down
MOVE U,OPRLDB## ;LINE JUST CRASHED. TELL OPERATOR
PUSHJ P,INLMES## ; ABOUT THIS
ASCIZ /%% Maintenance message received on /
PUSHJ P,PRDMR ;PRINT THE LINE NUMBER
PUSHJ P,INLMES## ;WE ALSO WANT TO TIME-STAMP THIS
ASCIZ / at /
PUSHJ P,PRDTIM## ;SO PRINT THE TIME OF DAY.
RCVMA0: LDB T1,PDRSTS ;Get status now
MOVEI T2,DR%MAI ;GET THE MAINTENANCE STATE
DPB T2,PDRSTS ; AND SET THIS LINE TO IT.
CAILE T1,DR%MAI ;If now running (or starting)
PUSHJ P,[DPB T1,PDRSTS ;Restore current status
PUSHJ P,DMRPDN ;Free our buffers, tell user
MOVEI T2,DR%MAI ;Now, assume user will cooperate
DPB T2,PDRSTS ; ...
MOVEI T1,KI.MAI ;MM received code
PJRST CALUSR] ;Tell user
LDB T1,PDRSTS ;Get (possibly changed) state
CAIE T1,DR%MAI ;And if user didn't leave us in Maint mode
PJRST RCVMAR ;well, then, toss the message (Must restart)
MOVEI T1,DMRSMT ;The start in maintenance bit
IORM T1,DMRSTS(W) ;Where to set it
RCVMAR: PUSHJ P,DMRINI ;Restart the DMR in the new mode
JFCL ;(It halted itself)
POPJ P, ;And it tossed the message
;Called from RCVMSG with:
; F, W := COMM and DMR pointers
; T3 := character count of received buffer
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVMA1: JUMPE T3,RCVBUF ;IF EMPTY MSG, DON'T GIVE IT TO NETSER
PUSHJ P,GETBUF ;Get a receive buffer
JUMPE T1,RCVABT ;If refused, forget it
AOS DMRRMC(W) ;Another message received
MOVE T2,MB.FMS(T1) ;Get pointer to MSD
JUMPE T2,[MOVSI T2,(POINT 8,0) ;If ANF style
HRRI T2,STCDAT(T1) ;make a byte pointer
JRST RCVMA2] ;And go copy
CAMLE T3,MD.BYT(T2) ;Make sure we got a nice, big buffer
PUSHJ P,NTDSTP## ;++ User gave too small a buffer
MOVEM T3,MD.BYT(T2) ;Store the actual byte count
MOVE T6,MD.ALA(T2) ;Byte pointer is indexed by T6
MOVE T2,MD.PTR(T2) ;Fetch pointer to first byte
RCVMA2: SOJGE T3,[PUSHJ P,ILDCB ;Get a byte from DMR buffer
IDPB S,T2 ;Copy into user's buffer
JRST RCVMA2];And loop till done
MOVE T3,T1 ;Copy address of buffer
MOVEI T1,KI.INC ;Input complete
PUSHJ P,CALUSR ;Call user
PJRST RCVBUF ;Go fetch another buffer
SUBTTL RCVSTR -- START received control out from DMR
;Here when START received control out from DMR
RCVSTR: MOVE U,OPRLDB## ;WE WANT TO TELL THE OPR, GET HIS LDB ADDR
PUSHJ P,INLMES## ;SEND FIRST PART OF MESSAGE
ASCIZ /%% Unexpected restart on /
PUSHJ P,PRDMR ;PRINT THE FULL DMR#
PUSHJ P,INLMES## ;FINISH MESSAGE OFF WITH TIME OF DAY
ASCIZ / at /
PUSHJ P,PRDTIM## ;PRINT TIME AND CRLF
PJRST DMRKIL ;Restart the line
SUBTTL RCVDAT -- ROUTINE TO PROCESS INCOMING DATA MESSAGES.
;Called from RCVMSG with:
; F, W := COMM and DMR pointers
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
; T3 := Byte count of message
;
RCVDAT: LDB T1,PDRSTS ;Get current state
CAIGE T1,DR%MAI ;If not in some sort of protocol
PJSP T1,DMRKIL ;Initiate a halt sequence
MOVEI T2,DR%RUN ;Just in case...
CAIE T1,DR%WT1 ;If in a start sequence
CAIN T1,DR%WTD ; (this is one too...)
DPB T2,PDRSTS ;Then we now know that the line is up
;(And our previous lie was harmless...)
JUMPE T3,RCVBUF ;If message is null, ignore it
PUSHJ P,GETBUF ;Get a buffer (arg in , yes, T3)
JUMPE T1,RCVABT ;If can't get one, simply starve the DMR
;(If it takes long enf, it will NAK some
;other message till we give it a new one...)
AOS DMRRMC(W) ;Another message received
PUSH P,T1 ;Save our pointer to the buffer
;Figure out what sort of message we got...
MOVE T2,MB.FMS(T1) ;Get the MSD pointer
JUMPE T2,RCVDA1 ;If none, we are filling a PCB
;Message is DECnet
CAMLE T3,MD.ALL(T2) ;Make sure the data will fit
PUSHJ P,NTDSTP## ;User gave us too small a buffer
MOVEM T3,MD.BYT(T2) ;Store the byte count
MOVE T6,MD.ALA(T2) ;Byte pointer is indexed by T6
MOVE T2,MD.PTR(T2) ;Fetch the byte pointer
JRST RCVDA2 ;Now go copy the data
;Message is ANF
RCVDA1: HRRZ T2,PCBICT(T1) ;GET THE LENGTH OF THE INPUT PCB
CAILE T3,(T2) ;MAKE SURE THIS MSG WILL FIT
PJSP T1,DMRERR ;DMR should have NAK'd
HRRM T3,PCBICT(T1) ;RETURN THE LENGTH TO NETSER
MOVE T2,PCBIAD(T1) ;GET THE BYTE POINTER TO FILL THE PCB WITH
; JRST RCVDA2 ;Now, go copy the data
;AT THIS POINT
; T2 := A BYTE POINTER INTO THE PCB
; T3 := THE NUMBER OF BYTES TO MOVE
; T4 := A CBP INTO THE MESSAGE TO COPY.
;FOR EFFICIENCY, COPY AS MANY SETS OF FOUR BYTES AS POSSIBLE.
RCVDA2: MOVEM T3,T1 ;GET A COPY OF THE COUNT
LSH T3,-2 ;GET THE NUMBER 4 BYTE SETS TO COPY
RCVDA3: SOJL T3,RCVDA4 ;COUNT OFF 4 MORE BYTES
MOVS S,(T4) ;GET BYTES #1 AND #0
IDPB S,T2 ;STORE #0
LSH S,-^D8 ;SHIFT #1 DOWN
IDPB S,T2 ; AND STORE THAT
LSH S,-^D<8+2> ;GET BYTES #3 AND #2
IDPB S,T2 ;STORE #2
LSH S,-^D8 ;SHIFT #3 DOWN
IDPB S,T2 ; AND STORE THAT
AOJA T4,RCVDA3 ;ADVANCE THE CBP 4 BYTES AND GO DO NEXT SET
;HERE TO COPY THE LAST 3 OR FEWER BYTES
RCVDA4: ANDI T1,3 ;GET THE COUNT MODULO 4
PUSHJ P,DLDCB ;PRE-DECREMENT THE CBP SINCE AOJA IS 1 TOO FAR
RCVDA5: SOJL T1,RCVDA6 ;COUNT DOWN THE NEXT BYTE
PUSHJ P,ILDCB ;LOAD A CANNONICAL BYTE
IDPB S,T2 ; AND STORE IT
JRST RCVDA5 ;LOOP OVER THE LAST FEW
;Copy complete. Give up the DMR's buffer NOW because:
; - We're done with it
; - D8RRDD would try to dequeue it again from FEKINT
;Then tell NETSER that a message has arrived
RCVDA6: PUSHJ P,RCVBUF ;Release the DMR's Buffer
POP P,T3 ;Get buffer address
MOVEI T1,KI.INC ;GET INPUT DONE FUNCTION CODE
PJRST CALUSR ;CALL USER
SUBTTL RCVBUF -- POST AN INPUT BUFFER RECEIVE REQUEST
; This routine:
; 1) Checks for a free input buffer. If none, it returns.
; 2) Allocates the input buffer and marks it as "in use".
; 3) Queue's a RBA/CCI transaction for the DMR11
;
;Called from "many places" with:
; F, W := COMM and DMR pointers
;
RCVBUF: LDB T1,PDRSTS ;GET OUR STATE
MOVEI T2,DMRSTV ;If we're starving this line now,
TDNN T2,DMRSTS(W) ; then we don't feed the DMR
CAIGE T1,DR%MAI ;Running?
POPJ P, ; IF WE CAN'T, JUST EXIT NOW
AOSG T1,DMRRBC(W) ;INCREMENT COUNT OF RCV BUFFERS QUEUED
PUSHJ P,NTDSTP## ;++ SHOULD NEVER BE NEGATIVE
CAILE T1,DR%NBF ;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
JRST [SOS DMRRBC(W) ;CORRECT THE COUNT
POPJ P,] ; AND RETURN
PUSHJ P,SAVE4## ;Save ACs in case coming through FEKser
AOS P4,DMRRBG(W) ;Get next buffer to give
CAIL P4,DR%NBF ;Time to wrap?
SETZB P4,DMRRBG(W) ;Yes, do so now
MOVE T1,P4 ;Save buffer number for 11 address
IMULI P4,TBFSIZ ;Compute buffer offset
ADDI P4,TBFIN0(F) ;Make 10 address
IMULI T1,EBFSIZ ;11 Buffer offset
ADDI T1,EBFIN0 ;Relocate to buffer space
ADD T1,DMREAD(W) ;Make 11 physical address
MOVSI T2,(1B0) ;GET A SIGN BIT AND USE IT TO MAKE SURE
TDNE T2,(P4) ; THAT THIS BDL IS REALLY FREE
PUSHJ P,NTDSTP ;++ SOMETHING SCREWED UP...
IORM T2,(P4) ;SET BDL IN USE CHECK BIT
;DROP TRHOUGH TO CONSTRUCT THE RBA/CCI Command
;FROM ABOVE. HERE TO CONSTRUCT AND QUEUE THE BUFFER-IN TRANSACTION
LSHC T1,-^D16 ;Put low 16 address bits in SEL 4
LSH T2,-2 ;Skip bits 0 & 1
HRRI T2,EBFSIZ ;Put CC in SEL6
DPB T1,[POINT 2,T2,21] ;Insert high address bits
MOVEI T1,DMRRBI ;RBA/CCI command
;NOW QUEUE THE BUFFER IN TRANSACTION
PUSHJ P,DMRINP ;QUEUE TRANSACTION
POPJ P, ;AND RETURN
POPJ P, ;(IGNORE SKIP RETURN)
SUBTTL XMTBUF -- ROUTINE TO TRANSMIT DDCMP MESSAGES.
;Called from "most anywhere" with interrupts off and:
; F, W := COMM and DMR pointers
;
XMTBUF: PUSHJ P,SAVE4## ;THIS CODE CLOBBERS THEM ALL
LDB P1,PDRSTS ;GET OUR STATE
CAIGE P1,DR%MAI ;If not running,
POPJ P,
SKIPN DMRWTO(W) ; we need a message to send
POPJ P, ;and we don't have one
AOSG T1,DMRCTA(W) ;INCREMENT COUNT OF XMIT BUFFERS QUEUED
PUSHJ P,NTDSTP## ;++ SHOULD NEVER BE NEGATIVE
CAILE T1,DR%NBF ;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
JRST [SOS DMRCTA(W) ;CORRECT THE COUNT
POPJ P,] ; AND RETURN
AOS P4,DMRXBG(W) ;Get number of next buffer to send
CAIL P4,DR%NBF ;Wrap around time?
SETZB P4,DMRXBG(W) ;Yes, do so
IMULI P4,TBFSIZ ;Make into buffer offset
ADDI P4,TBFOU0(F) ;10 virtual address
MOVSI T1,(1B0) ;GET A SIGN BIT AND USE IT TO MAKE SURE
TDNE T1,(P4) ; THAT THIS BDL IS REALLY FREE
PUSHJ P,NTDSTP ;++ SOMETHING SCREWED UP...
IORM T1,(P4) ;SET BDL IN USE CHECK BIT
SUBTTL XMTDAT -- ROUTINE TO SEND A DATA MESSAGE.
; XMTDAT sends DDCMP data messages. It:
; 1) Requeues the PCB of the message it is outputing
; to the "awaiting ACK" (DMRWTA) queue. (We are
; interlocked, so there is no fear of races.)
; 2) Copies the "header" portion of the message to the
; correct output buffer in the DMR page.
; 3) Copies, with compression, the "data"
; portion of the PCB. (Ie. The PCBOA1 data.)
; 4) It queues a TBA/CCI transaction for the DMR11
;
;Called from XMTBUF with:
; F, W := COMM and DMR pointers
; P4 := The address of the BUFFER to use for the message.
;
;Register usage.
; S = The character we are currently hacking
; T1 = Count of chars left in pcb
; T2 = Byte pointer into the pcb
; T3 = Total number of chars put in the output message buffer
; T4 = A "canonical" byte pointer to the output message buffer
;
; P1-P3 = "Scratch" registers for the conversion routines.
; P4 = Pointer to TBFOU?. Ie. the beginning of the buffer
;
; U = Pointer to the buffer
; F = Pointer to Comm area
; W = Pointer to DMR block
;
XMTDAT: PUSH P,U ;Save U for the duration
HRRZ U,DMRWTO(W) ;GET THE NEXT PCB AWAITING OUTPUT
HRRZ T1,MB.NXT(U) ;GET THE ADDRESS OF THE ONE AFTER THAT
HRRZM T1,DMRWTO(W) ; AND MAKE THAT THE "NEXT" AWAITING OUTPUT
;FIRST PUT THE BUFFER ON THE END OF THE "AWAIT ACK" QUEUE
HLLZS MB.NXT(U) ;MAKE SURE THERE ARE NO STRAY POINTERS
HRRZ T1,DMRWTA(W) ;GET THE FIRST PCB IN THE AWAIT ACK QUEUE
JUMPE T1,[HRRZM U,DMRWTA(W) ;IF THERE AREN'T ANY, MAKE THIS THE FIRST
JRST XMTDA2] ; AND CONTINUE WITH MAIN CODE.
XMTDA1: HRRZ T2,MB.NXT(T1) ;T2 := THE "NEXT" PCB
JUMPE T2,[HRRM U,MB.NXT(T1) ;IF THERE ISN'T ONE, MAKE "U" THE LAST
JRST XMTDA2] ; AND CONTINUE WITH MAIN CODE.
MOVEI T1,(T2) ;If not at end of queue, go to next buffer
JRST XMTDA1 ;LOOP UNTIL WE FIND THE END OF THE QUEUE.
; FROM HERE ON, THE FOLLOWING CONVENTIONS GENERALLY HOLD
; T3 := COUNT OF THE NUMBER OF BYTES SO FAR
; T4 := A BYTE POINTER (PDP-10 STYLE) TO THE OUTPUT BUFFER.
; WHEN WE ARE ALL DONE COPYING THE DATA, WE WILL SWAP THE BYTES
; ALL AT ONCE.
XMTDA2: MOVE T2,MB.FMS(U) ;Get pointer to first MSD
JUMPE T2,XMTANF ;If none, must be an ANF-10 message
MOVSI T4,(POINT 8,) ;Start off with T4 being a byte pointer
HRRI T4,0(P4) ; to the output buffer
PUSH P,T0 ; Needed for extend (really U)
PUSH P,[EXP 0] ;Save total bytes in message
XMTDEC: MOVE T0,MD.BYT(T2) ;GET THE LENGTH OF THIS SEGMENT
MOVE T6,MD.ALL(T2) ;GET ALLOCATED LENGTH
CAMLE T0,T6 ;IS THE MESSAGE OVERLOADED?
PUSHJ P,NTDSTP## ;++ DECNET MESSAGE SEGMENT OVERLOADED
MOVE T6,MD.ALA(T2) ;BYTE POINTER IS INDEXED BY T6
MOVE T1,MD.AUX(T2) ; AND IT'S BYTE ADDRESS
MOVE T3,T0 ;COPY THE LENGTH FOR THE DESTINATION
ADDM T3,(P) ; AND ACCUMULATE THE TOTAL COPIED
EXTEND T0,[MOVSLJ] ;COPY THE SEGMENT
PUSHJ P,NTDSTP## ;++ HARDWARE BROKEN
HRRZ T2,MD.NXT(T2) ;STEP TO THE NEXT SEGMENT
JUMPN T2,XMTDEC ; AND IF THERE IS ONE, COPY THAT TOO
POP P,T3 ;T3 := THE TOTAL LENGTH
POP P,T0 ;RESTORE "U"
CAILE T3,DMRMMS*4 ;MAKE SURE THE LENGTH WAS REASONABLE
PUSHJ P,NTDSTP## ;++ MESSAGE WAS TOO BIG
JRST XMTXIT ;GO SWAP THE BYTES AND SEND THE MESSAGE
;HERE FOR ANF-10 STYLE BUFFERS
XMTANF: LDB T1,PDRSTS ;FIRST GET THE STATE, AND
CAIN T1,DR%MAI ;IF THIS IS A MAINT MESSAGE
JRST XMTANM ; GO PROCESS THE STC-BLOCK FORMAT
;NOW COPY THE "NCL HEADER" PORTION OF THE MESSAGE.
; (SNEAKILY MAKING USE OF THE FACT THAT PCB'S START ON WORDS)
HRRZ T1,PCBOCT##(U) ;GET THE LENGTH OF THE HEADER
MOVE T3,T1 ;GET A "FINAL" COPY OF THE LENGTH
MOVE T4,T1 ; AND A COPY TO USE FOR ADJBP.
HLRZ T2,PCBOAD##(U) ;GET THE PCB'S BYTE POINTER
CAIN T2,(POINT 8,0) ; AND IF IT'S NOT A NEW 8 BIT POINTER
SKIPG T1 ; OR THE COUNT IS .LE. 0
PUSHJ P,NTDSTP## ;LET SOMEONE KNOW ABOUT IT.
SOS T1 ;ROUND DOWN, AND SHIFT TO GET THE
LSH T1,-2 ; NUMBER OF WORDS TO BLT (LESS 1)
MOVSI T2,(1B0) ;Get buffer in use bit
TDNN T2,0(P4) ;Make sure its set
PUSHJ P,NTDSTP## ;++ Failed to interlock buffer before sending
HRLZ T2,PCBOAD##(U) ;GET THE ADDRESS TO START BLT'ING FROM
HRRI T2,(P4) ;GET THE ADDRESS TO BLT TO
ADDI T1,(T2) ;ADDRESS OF LAST WORD TO BLT TO
BLT T2,(T1) ;TRANSFER THE ENTIRE HEADER (AND
; POSSIBLY A FEW EXTRA BYTES...)
MOVSI T1,(POINT 8,0) ;GET A SKELETON BYTE POINTER
HRRI T1,(P4) ; MAKE IT POINT TO THE OUTPUT BUFFER
ADJBP T4,T1 ;ADVANCE IT TO ACCOUNT FOR THE DATA
; WE JUST PUT IN THE BUFFER.
;NOW THE "NCL HEADER" HAS BEEN COPIED, READ CONVERSION CODE AND COPY "DATA"
LDB T1,PCBPCV## ;GET THE CONVERSION CODE
CAIL T1,PCV.NC ;RANGE CHECK
CAILE T1,PCV.BN ; THE CONVERSION CODE
PUSHJ P,NTDSTP## ;++ NETSER GAVE GARBAGE
SKIPE PCBOC1(U) ;IF NO SECOND BUFFER, DONT TRY TO SEND IT
PUSHJ P,@[EXP C.NC,C.LP,C.BN](T1) ;CALL CONVERSION ROUTINE
JRST XMTXIT ;Go swap bytes and send it
;Here to send an ANF-10 maint message (STC block)
XMTANM: HLRZ T3,STCBLK(U) ;Get byte count where XMTXIT needs it
CAILE T3,DMRMMS*4 ;If unreasonable, should have been caught
PUSHJ P,NTDSTP## ;++ MM too long! (by NETSER...)
MOVEI T1,0(P4) ;Point to the buffer we chose
HRLI T1,STCDAT(U) ;Address of data to go
MOVEI T2,-1(T3) ;Len (bytes) Rnd up (+3) - 1 wd (-4)
LSH T2,-2 ;Convert to words
ADDI T2,(T1) ;End address for BLT
BLT T1,(T2) ;Copy data into buffer
; JRST XMTXIT ;Swap bytes and send message
;NOW WE HAVE THE BUFFER FILLED, BUT WE MUST SWAP THE BYTES FOR THE
; STUPID UBA...
XMTXIT: MOVNI T1,3(T3) ;GET MINUS THE NUMBER OF BYTES (ROUND UP)
ASH T1,-2 ;CONVERT TO A WORD COUNT
HRL T4,T1 ;SET UP THE LH OF THE AOBJN POINTER
HRRI T4,(P4) ;SET UP THE RH TO POINT TO THE BUFFER
XMTSWP: MOVE T1,(T4) ;GET THE NEXT SET OF 4 BYTES
LSH T1,-4 ;POSITION THE "LAST" BYTE
DPB T1,BYTABL+3 ; AND STORE IT
LSH T1,-^D8 ;POSITION THE NEXT ONE
DPB T1,BYTABL+2 ; AND STORE THAT
LSH T1,-^D8 ;GET THE NEXT TO FIRST
DPB T1,BYTABL+1 ; ..
LSH T1,-^D8 ;GET THE FIRST
DPB T1,BYTABL+0 ; ..
REPEAT 0,< ;[DBUG] Waste CPU time and clear 10 only bits
MOVE T1,[600K,,600K] ;This makes looking at xmit buffers
ANDCAM T1,(T4) ;Much easier when debugging
>;End REPEAT 1
AOBJN T4,XMTSWP ;LOOP OVER THEM ALL
;Now, figure out what to tell the DMR
MOVSI T1,(1B0) ;First, reset the buffer in use bit
IORM T1,0(P4) ;Since the conversion code (BLT) stomped on it
MOVEI T1,(T3) ;COPY THE MESSAGE LENGTH
POP P,U ;Restore U
PUSHJ P,XMTBDH ;WRITE HEADER, BDL AND QUEUE OUTPUT TO DMR
PJSP T1,DMRERR ;Error, reload dmr
POPJ P, ;SUCCESS
SUBTTL XMTBDH -- Build and Queue DMR transaction for output
;XMTBDH Routine to Build and Queue DMR transaction for output
;CALL T1 := LENGTH OF MESSAGE IN BYTES
; F, W, P4 := SET UP AS FOR CALL TO XMT???
;RETURN CPOPJ IF DMR11 IS DEAD
; CPOPJ1 IF BUFFER-OUT TRANSACTION SUCCESSFULY QUEUED
XMTBDH: HRRZ T2,T1 ;Get Low-order bits of count
CAIG T1,EBFSIZ ;Message too big?
TRZE T2,740K ;DDCMP max is 14 bits
PUSHJ P,NTDSTP ;Attempt to send ridiculous message
MOVEI T1,(P4) ;Data address
SUBI T1,(F) ;In-page address
LSH T1,2 ; in 11 bytes
ADD T1,DMREAD(W) ;Make into 11 address
DPB T1,[POINT 16,T2,17] ;Low address for SEL 4
LSH T1,-^D16 ;Get high address
DPB T1,[POINT 2,T2,21] ;Put in high 2 bits of SEL6
MOVEI T1,DMRTBI ;Command we want executed
PJRST DMRINP ;WILL SKIP IF DMR STILL RUNNING
SUBTTL ANF-10 specific conversion/compression routines
;C.NC COPY. NO CONVERSION
T0==T1-1 ;REALLY IS "U" (WE NEED 5 AC'S FOR EXTEND)
C.NC: PUSH P,T0 ;SAVE OUR TEMP
MOVE T1,PCBOA1(U) ;GET THE BYTE POINTER FOR IT
MOVE T0,PCBOC1(U) ;GET THE LENGTH OF THE STRING TO MOVE
ADD T3,T0 ;UPDATE T3 TO REFLECT IMPENDING COPY
PUSH P,T3 ;SAVE IT FOR A BIT
MOVE T3,T0 ;MAKE SOURCE AND DEST LENGTHS THE SAME
EXTEND T0,[MOVSLJ] ;MOVE THE SLUDGE
PUSHJ P,NTDSTP## ;++ HARDWARE IS BROKEN?
POP P,T3 ;UPDATE THE COUNT OF BYTES IN THE BUFFER
POP P,T0 ;RESTORE OUR "TEMP"
POPJ P, ; AND WE'RE DONE (WASN'T THAT EASY...)
;C.BN COPY. BINARY CONVERSION
C.BN: HRRZ T1,PCBOC1(U) ;GET COUNT OF SECONDARY BUFFER
MOVE T2,PCBOA1(U) ;GET ADDRESS OF SECONDARY BUFFER
C.BN1: SOJL T1,C.BN2 ;LOOP OVER ALL OUTPUT BYTES
ILDB P1,T2 ;GET FIRST 12 BIT BYTE
LDB S,[POINT 8,P1,31] ;GET FIRST 8 BITS (4 BITS LEFT)
IDPB S,T4 ; AND STORE THEM
SOJL T1,C.BNX ;IF WE COUNT OUT NOW, SEND LAST 4 BITS
ILDB P2,T2 ;GET NEXT 12 BIT CHAR
LDB S,[POINT 4,P2,27] ;GET 4 LOW BITS FROM NEW 12 BIT BYTE
DPB P1,[POINT 4,S,31] ;PUT 4 HIGH BITS FROM OLD 12 BIT BYTE
IDPB S,T4 ; AND STORE THEM
LDB S,[POINT 8,P2,35] ;GET LAST 8 BITS FROM NEW BYTE
IDPB S,T4 ; AND GET RID OF THE LAST
JRST C.BN1 ;LOOP OVER ALL 12 BIT BYTES
C.BNX: DPB P1,[POINT 4,S,31] ;HERE IF ODD NUMBER, GET LAST 4 BITS
ANDI S,360 ;MASK TO ONLY 4 BITS
IDPB S,T4 ; AND WEVE MOVED ALL THE DATA
;NOW TO ADJUST "T3" TO ACCOUNT FOR THE BYTES JUST STORED
C.BN2: MOVE T1,PCBOC1(U) ;GET THE COUNT BACK
IMULI T1,^D12 ; (12 * PCBOCT) + 7
ADDI T1,7 ; ----------------- = NUMBER OF 8 BIT BYTES
LSH T1,-3 ; 8
ADDI T3,(T1) ;UPDATE THE COUNT
POPJ P, ;DONE, T4 CONTAINS UPDATED BYTE POINTER
;C.LP COPY. LINE PRINTER CONVERSION (WITH COMPRESSION)
;
;DATA FOR LINE PRINTER IS COMPRESSED AS FOLLOWS
; 1CCCCCCC ;CCCCCCC IS A SINGLE CHARACTER
; 01XXXXXX ;XXXXXX IS A COUNT OF BLANKS
; 001XXXXX ;XXXXX IS A REPETITION COUNT FOR NEXT CHAR
;
;REGISTER USAGE
; P1 := REPEAT COUNT
; P2 := CHAR BEING REPEATED
; P3 := NEXT CHAR (PUT HERE & COMPARED WITH P2)
C.LP: HRRZ T1,PCBOC1(U) ;GET COUNT OF SECONDARY DATA
MOVE T2,PCBOA1(U) ;GET POINTER TO SECONDARY DATA
PUSH P,T4 ;SAVE BYTE POSITION AND COUNT. WE WILL
PUSH P,T3 ; HAVE TO "FIXUP" THE NCL HEADER TO ACCOUNT
; FOR THE LENGTH DIFFERENCE AFTER COMPRESSION.
;FROM ABOVE. NOW START COMPRESSING THE LINE-PRINTER DATA.
SETZ P1, ;INITIALIZE REPEAT COUNT
SOJL T1,C.LPX ;IF NO DATA, FIX UP MSG LENGTH ANYWAY
ILDB P2,T2 ;PRIME THE LOOP WITH AN INITIAL CHAR
LPLOOP: SOJL T1,NMATCH ;HO CHARS DON'T MATCH ANY CHARS
ILDB P3,T2 ;GET NEXT CHAR
CAIN P3,(P2) ;SAME AS LAST?
AOJA P1,LPLOOP ;IF SO, COUNT IT AND KEEP SCANNING
NMATCH: JUMPE P1,SINGLE ;JUMP IF THIS WAS AN UN-REPEATED CHAR
AOJ P1, ;FIXUP THE COUNT (WAS OFF BY 1)
CAIN P2," " ;WERE WE COMPRESSING SPACES?
JRST SPACES ;IF IT WAS SPACES, HANDLE DIFFERENTLY
CHARS: CAIG P1,37 ;MORE CHARS THAN 1 BYTE CAN REPEAT?
JRST CHARX ;IF IT WILL FIT IN 1 BYTE, SEND NOW
MOVEI S,77 ;MAXIMUM REPEAT COUNT & CHAR FLAG
IDPB S,T4 ;WRITE THE REPEAT COUNT
IDPB P2,T4 ; AND NOW WRITE THE CHARACTER
ADDI T3,2 ;ACCOUNT FOR THE TWO BYTES WRITTEN
SUBI P1,37 ;ACCOUNT FOR 37 LESS CHARS IN REPEAT COUNT
JRST CHARS ;LOOP TILL REPEAT COUNT GETS SMALL ENOUGH
CHARX: MOVEI S,40(P1) ;GET REPEAT CHAR BYTE
IDPB S,T4 ;STORE THE REPEAT COUNT
TRO P2,200 ;TURN ON HIGH BIT TO KEEP 8'S HAPPY
IDPB P2,T4 ; AND NOW THE CHARACTER
ADDI T3,2 ;ACCOUNT FOR BOTH
JRST ADVNC1 ; AND GO BACK TO COMPARE LOOP
SPACES: CAIG P1,77 ;SEE IF WE CAN FIT THIS ALL IN 1 REPEAT COUNT
JRST SPACEX ;JUMP IF 1 REPEAT COUNT BYTE IS SUFFICIENT
MOVEI S,177 ;GET A "77" SPACES REPEAT BYTE
IDPB S,T4 ;STORE THE SPACE REPEAT COUNT
AOS T3 ; AND ACCOUNT FOR IT
SUBI P1,77 ;COUNT OFF THE 77 SPACES
JRST SPACES ;LOOP TILL COUNT GETS SMALL ENOUGH
SPACEX: MOVEI S,100(P1) ;GET "SPACE REPEAT" BIT AND COUNT
JRST ADVNCE ;WRITE THE BYTE AND GO BACK TO COMPARE LOOP
SINGLE: MOVEI S,200(P2) ;GET THE SINGLE CHAR BIT (AND THE CHAR)
ADVNCE: IDPB S,T4 ;STORE THE BYTE
AOS T3 ; AND ACCOUNT FOR IT
ADVNC1: MOVEI P2,(P3) ;ADVANCE THE CHAR TO ATTEMPT A MATCH ON
SETZ P1, ;CLEAR THE COUNT
JUMPGE T1,LPLOOP ;GO BACK IF THERE ARE MORE CHARS TO DO
; JRST C.LPX ;IF DONE, GO FIXUP NCL HEADER (SIGH)
;C.LPX At this point the line printer data has been compressed.
; T3 contains the actual number of data bytes. (P) contains
; the number of bytes in the "NCL HEADER" portion of the
; message. (This includes the "type" field)
;
C.LPX: POP P,T2 ;GET LENGTH OF HEADER
SUBM T3,T2 ;SUBTRACT TO GET LENGTH OF DATA
ADDI T2,1 ;COUNT THE TYPE FIELD TWICE. (WE JUST
; SUBTRACTED IT OUT ONCE.)
EXCH T4,(P) ;GET A BYTE POINTER TO LAST "CNT" BYTE
PUSHJ P,C.DLB ;SKIP BACKWARDS OVER THE "TYPE" FIELD
MOVEI T1,0 ;INITIALIZE LENGTH OF THE "CNT" FIELD
C.LPX1: PUSHJ P,C.DLB ;DECREMENT AND LOAD THE BYTE
TRNE S,200 ;IS THE EXTENSIBLE BIT ON?
AOJA T1,C.LPX1 ;IF STILL EXTENSIBLE, THEN STILL IN "CNT"
C.LPX2: MOVEI S,(T2) ;GET A COPY OF T2 (DATA BYTE COUNT)
LSH T2,-7 ;SHIFT COUNT DOWN FOR NEXT TIME
ANDI S,177 ;GET JUST 7 BITS WORTH
SKIPE T1 ;BUT IF THIS ISN'T THE LAST BYTE
TRO S,200 ; THEN SET THE EXTENSIBLE BIT
IDPB S,T4 ;STORE THE BYTE
SOJGE T1,C.LPX2 ;LOOP OVER ALL OF THE "CNT" FIELD
SKIPE T2 ;WE BETTER HAVE STORED IT ALL
PUSHJ P,NTDSTP## ;++ HORRIBLE BUG.
POP P,T4 ;GET POINTER TO MESSAGE BACK AGAIN
POPJ P, ;DONE WITH LPT COMPRESSION
; T3 := LENGTH OF ENTIRE MESSAGE BUFFER
;ROUTINE TO DO A "DECREMENT AND LOAD BYTE"
C.DLB: MOVE S,T4 ;COPY THE BYTE POINTER
SETO T4, ;GET A -1
ADJBP T4,S ;BACK IT UP
LDB S,T4 ;GET THE BYTE
POPJ P, ; AND RETURN
SUBTTL GETBAD -- ROUTINE TO FIND A BUFFER GIVEN IT'S UNIBUS ADDRESS.
;Called from D8R?O? with:
; F, W := COMM and DMR pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the DMR11 (Which contain
; the unibus address of a BUFFER)
;Returns
; CPOPJ With T4 := CBP pointing to the BUFFER described in P2 & P3.
; T3 := Byte count of message
;
GETBAD: MOVE T1,P2 ;16 Bit address
HRLZ T4,P3 ;High bits and count
SETZ T3, ;Clear hi bits
LSHC T3,4 ;Get 2 high bits (skip 2 10-only bits)
LSH T3,^D16 ;Shift to end of word (B16 & B17)
IORI T1,(T3) ;18 bit unibus address
SUB T1,DMREAD(W) ;Convert to page offset
DPB T1,[POINT 2,T1,17-2] ;Byte in 10 word to LH
LSH T1,-2 ;Now should have CBP
HRRZ T3,T1 ;Get page offset
CAIGE T3,DMRPPL*1K ;If greater than end of region
TLNE T1,^-3 ; or if bits other than byte # set
PUSHJ P,NTDSTP## ;++ Miscomputed byte pointer
ADDI T1,(F) ;Relocate CBP to 10 virtual address
SETZ T3, ;Clear count
LSHC T3,^D14 ;Put received count in T3
MOVE T4,T1 ;Copy CBP into T4
POPJ P, ;Done
Subttl DMRINI -- Routine to Crank up a DMR11
;Here at once-only time and for most fatal errors
DMRINI: PUSHJ P,SAVE2## ;Save our working ACs
AOS DMRICT(W) ;Count attempted restarts
MOVEI T1,DMRMRC ;Get Mister Clear
WRIO T1,@DMRCSR(W) ;Start over
MOVEI T2,4000 ;Snooze time
MOVEI T1,DMRRUN ;The run bit
DMRILP: TION T1,@DMRCSR(W) ;Set yet?
SOJG T2,DMRILP ;No, wait for it (Spec says 6.4 ms)
MOVE P2,DMRCSR(W) ;Get CSR address
RDIOB T1,DMRBS3(P2) ;Read diagnostic results
CAIE T1,DMRMDI ;If inhibited
CAIN T1,DMRMDR ;or ran successfully
JUMPG T2,DMRSTR ;Start the DMR
;The DMR timed out or failed microdiagnostics
SETZ T1, ;Clear run, IEI
WRIO T1,DMRSL0(P2) ; ...
WRIOB T1,DMRBS2(P2) ;and IEO
POPJ P, ;Error return
;The DMR is running and needs BASEIN/CONTROLIN
DMRSTR: SKIPN F,DMRTAD(W) ;Get 10 address
PUSHJ P,NTDSTP## ;DMR block was smashed
DMROFF ;Prevent various races
HRLZI T1,(F) ;Clear out comm region
HRRI T1,1(F) ;...
SETZM (F) ;
BLT T1,COMSIZ-1(F) ;So we start fresh
MOVE T1,DMRFEK(W) ;Get FEK address
HRRM W,FEKUNI(T1) ;Be sure FEK points to DMR
MOVEI T1,M.DN60## ;See if IBMcom loaded
JUMPE T1,DMRST1 ;If not, skip this
HRRZ T1,DMRLIN(W) ;Get this DMR number
MOVE T1,DMRBAS##(T1) ;Find the DLX block for this line
DMRST1: MOVEM T1,DMRDLX(W) ;Point to it
PUSHJ P,DEFCIR ;Define a DECnet circuit if necessary
MOVE T1,DMRVEC(W) ;Get vector address for this DMR
LSH T1,-2 ;Make into 10 word offset
HLRZ T2,DMRCSR(W) ;Get UBA number
ADD T1,.EPVIT##-1(T2) ;Compute address of vector table entry
MOVSI T2,(JSR) ;Interrupt instruction
HRRI T2,DMRIVA(W) ;Vector code
MOVEM T2,0(T1) ;'A' Vector instruction
HRRI T2,DMRIVB(W) ;Vector code
MOVEM T2,1(T1) ;'B' Vector instruction
MOVSI T1,DMRCLR(W) ;Clear out DMR dynamic area
HRRI T1,DMRCLR+1(W) ;...
SETZM -1(T1) ;...
BLT T1,DMRLEN-1(W) ;So counters, etc are reset
MOVEI T1,DR%WT1 ;Get waiting for first message state
DPB T1,PDRSTS ;Save
MOVEI T2,DMRPPL ;Get pages/line
MOVE T3,DMRMAP(W) ;Get address of the map
MOVEI T4,(F) ;Virtual address of com region
DMRST2: MAP T1,(T4) ;Find physical address of this page
TLZ T1,^-17 ;Clear nonexistant bits
LSH T1,W2PLSH## ;Make page number
IORI T1,UNBVBT!UNBD18 ;Valid mapping, 16 bit mode
WRIO T1,(T3) ;Make it accessible to the DMR
ADDI T4,PAGSIZ## ;Advance to the next page
AOJ T3, ;And the next mapping register
SOJG T2,DMRST2 ;Loop for all pages of com region
MOVEI T1,DMRIEI ;Enable input ready interrupts
BSIOB T1,DMRBS0(P2) ;From DMR
MOVEI T1,DMRIEO ;And output ready
BSIOB T1,DMRBS2(P2) ;Prior to BASE IN
MOVEI T1,DMRBSI ;BASE IN
HRRZ T2,DMREAD(W) ;Get 11 address
IFN EBASAD, ADDI T2,EBASAD ;Offset by base table start
HRLS T3,T2 ;Low in SEL4, high in SEL6
LSH T3,-2 ;Move bit 17 into bit 15
HRR T2,T3 ;Copy B14!B15 (other bits not valid)
AND T2,[177777,,B14!B15] ;Mask off unused bits
PUSHJ P,DMRINP ;Add to queue
PUSHJ P,NTDSTP## ;Can't be full!
MOVEI T1,DMRCTI ;CONTROL IN
SETZ T2, ;Default is zero
MOVE T3,DMRSTS(W) ;Get status bits for this DMR
TRNE T3,DMRSLS ;Select long start timer?
TRO T2,DMRILS ;Yes, set in SEL6
TRNE T3,DMRSHD ;Select HDX line?
TROA T2,DMRIHD ;Yes, set in SEL6
TRZ T2,DMRILS ;Not HDX, don't set long start
TRNE T3,DMRSMT ;Select maint mode?
JRST [TRO T2,DMRIMT ;Yes, tell DMR
MOVEI T3,DR%MAI ;Get maint state
DPB T3,PDRSTS ;and put line into it
JRST .+1]
PUSHJ P,DMRINP ;Add to Q
PUSHJ P,NTDSTP## ;Can't be full yet!
INILP2: MOVE T1,DMRRBC(W) ;Get current count of RBFs q'd
CAIL T1,DR%NBF ;Q'd all of them yet?
JRST INILP3 ;Yes, stop
PUSHJ P,RCVBUF ;No, queue one more
JRST INILP2 ;And see if done
INILP3: DMRON ;Allow interrupts again
PUSHJ P,DMRPUP ;Tell user protocol is up (even if it isn't)
;DMR hides when link goes up, so we lie a lot
JRST CPOPJ1## ;Done
SUBTTL DEFCIR -- DEFINE A DECNET CIRCUIT FOR A DMR
;This code requires that the call to DMRONC in SYSINI follow
;the call to D36INI at D36STR. Otherwise core for CB/Buffers unavailable.
DEFCIR: MOVEI T1,M.DECN## ;SEE IF DECNET IS LOADED
JUMPE T1,CPOPJ## ;IT IS NOT, WE CAN DO NIL
; HRRZ T1,DMRUSR(W) ;GET CURRENT LINE USER
; CAIE T1,DD.DEC ;IF NOT DECNET (IT ISN'T NECESSARY TO CHECK)
; POPJ P, ;DON'T PESTER ROUTER (WE MIGHT GET IT LATER)
MOVEI T1,KI.OPN ;INTERRUPT DRIVER TO CREATE CIRCUIT
MOVE T3,W ;GET POINTER TO KONTROLLER LINE BLOCK
HRRZ T4,DMRNUM(W) ;GET OUR DMR NUMBER
MOVSI T4,LD.DMR_^D9(T4) ;KONTROLLER TYPE + KON #
PUSHJ P,RTRDSP## ;TELL ROUTER ABOUT THIS LINE
SKIPN T1 ;WHERE WE SUPPLIED A CIRCUIT BY ROUTER?
PUSHJ P,NTDSTP## ;NOPE, WE'LL HAVE TO DIE
MOVEM T1,DMRLBK(W) ;STORE THE CIRCUIT POINTER
POPJ P, ;DONE
SUBTTL DMRINP -- ROUTINE TO QUEUE TRANSACTIONS FOR THE DMR11
;Called with:
; W := DMR pointer
; T1 := XWD 0,SEL0
; T2 := XWD SEL4,SEL6
;Returns
; CPOPJ DMR11 not running
; CPOPJ1 Transaction described by T1 & T2 has been queued.
; RQI has been set to request an interrupt on vector "A".
; DMRAIV will then process the top transaction on the queue.
;
DMRINU: DMROFF ;ENTRY TO TURN INTERRUPTS OFF FIRST
PUSHJ P,DMRINP ;QUEUE THE TRANSACTION
SKIPA ;HANDLE SKIP RETURNS PROPERLY
AOS (P) ;GIVE SKIP
DMRONJ::DMRON ;RE-ENABLE INTERRUPTS
POPJ P, ;ALL DONE
DMRINP: MOVE T3,DMRIQP(W) ;GET INDEX OF NEXT ENTRY IN THE QUEUE
LSH T3,1 ;MAKE IT AN OFFSET (ENTRYS ARE 2 WDS)
ADDI T3,DMRINQ(W) ;RELOCATE TO THE ADDRESS OF THE QUEUE
MOVEM T1,0(T3) ;STORE SEL0
MOVEM T2,1(T3) ;STORE XWD SEL4,SEL6
AOS T3,DMRIQP(W) ;ADVANCE THE "PUTTER"'S INDEX
CAIL T3,DMRIQN ;IF WE NEED TO WRAP AROUND, THEN
SETZB T3,DMRIQP(W) ; THEN WRAP TO THE FIRST ENTRY
CAMN T3,DMRIQT(W) ;IS THE QUEUE FULL (PUTTER = TAKER)
PJSP T1,DMRERR ; IF SO, DMR MUST BE DEAD. CRASH IT.
MOVEI T3,DMRRQI ;GET RQI AND SET IT IN BSEL0
BSIO T3,@DMRCSR(W) ; THIS WILL CAUSE A VECTOR "A" INTERRUPT
RETSKP ;GOOD RETURN
SUBTTL FREBOI - Free an input buffer
;FREBOI FREE AN INPUT BUFFER
; RETURN CANONICAL BYTE POINTER TO MESSAGE IN T4
; Returns byte count of message in T3
FREBOI: PUSHJ P,GETBAD ;GET T4 SET UP FROM P2, P3
TLNE T4,3 ;BUFFER DESCRIPTOR BETTER START ON EVEN -10 WD
POPJ P, ; IF NOT EVEN -10 ADDRESS, THEN DMR SCREWED UP
SKIPGE T1,0(T4) ; AND MAKE SURE THAT IT'S CLRd by DMR.
PUSHJ P,NTDSTP## ;WE'VE SCREWED UP THE BDL POINTERS [DBUG]
RETSKP
SUBTTL FREMAI - Free maint messages that have been output
;FREMAI ROUTINE TO FREE ANY MAINT MESSAGES
; MAINT MESSAGES MUST BE FREED OUTSIDE THE DMROFF/DMRON INTERLOCK WHICH
; IS NOT POSSIBLE IN D8RTXC.
;CALL W := DMR POINTER
;RETURN CPOPJ
FREMAI: LDB T1,PDRSTS ;If we're not in maint mode,
CAIE T1,DR%MAI ; ...
POPJ P, ;Don't confuse poor users (esp NETSER...)
FREMA1: DMROFF ;INTERLOCK WRT D8RTXC
HRRZ T3,DMRMAI(W) ;GET THE NEXT STC MSG TO FREE
JUMPE T3,FREMA2 ;If none, finish up
HRRZ T2,MB.NXT(T3) ;GET THE ADDRESS OF THE NEXT message TO FREE
HRRZM T2,DMRMAI(W) ; AND REMEMBER IT FOR NEXT TIME
SOSGE DMRMAC(W) ;Count down one fewer message
PUSHJ P,NTDSTP## ;++ Counts are wrong
DMRON ;GIVE BACK THE INTERLOCK
MOVEI T1,KI.ODN ;Say output done
PUSHJ P,CALUSR ; to the user
JRST FREMA1 ;Keep going
;Here with all messages freed
FREMA2: SKIPE DMRMAC(W) ;Better be none left
PUSHJ P,NTDSTP## ;++ Queue length didn't match count
DMRON ;Re-enable interrupts
POPJ P, ;Done at last
SUBTTL ERROR ROUTINES
;DMRERR - CALLED WHEN A DMR IS HOPELESSLY ILL.
;CALL JSP T1,DMRERR ;T1 CONTAINS CALLERS PC FOR DEBUGGING
;RETURN CPOPJ ; USUALLY TO CALLER'S CALLER
DMRERR::MOVEM T1,DMRCPC(W) ;SAVE THE CALLER'S PC
MOVE U,OPRLDB## ;WE HAD BETTER TELL SOMEONE ABOUT THIS...
PUSHJ P,INLMES## ;SEND THE OPERATOR A MESSAGE
ASCIZ /%% Fatal error on /
PUSHJ P,PRDMR ;TELL HIM WHICH DMR IT WAS.
PUSHJ P,PRSPC## ;Where we died...
MOVE T2,DMRCPC(W) ;GET THE CALLER'S PC BACK
SOS T2 ; BACK IT UP 1 LOCATION
PUSHJ P,PCP## ; AND PRINT IT AS A "PC"
PUSHJ P,INLMES## ;FINISH OFF THE MESSAGE
ASCIZ /. Line restarting
/
; PJRST DMRKIL ;STOP IT (CLEAR RUN)
;DMRKIL - ROUTINE TO CRASH A DMR11 (DMR) WHEN SOMETHING IS DRASTICALLY
; WRONG.
DMRKIL: PUSHJ P,DMRKL0 ;First kill the silly thing
PUSHJ P,DMRINI ;Then, restart it
JFCL ;Since it will probably recover
POPJ P, ;Done
DMRKL0: PUSHJ P,SAVE1## ;WE USE 1 P
MOVEI T1,DMRRUN ;GET THE RUN BIT
BCIO T1,@DMRCSR(W) ;Clear RUN. Don't master clear (would restart)
MOVEI T1,DMRSTV ;Get the starvation bit
ANDCAM T1,DMRSTS(W) ;And save a wasted call
MOVE T2,DMRCSR(W) ;GET ADDRESS OF THE DMR11 CSR'S
MOVE T3,[POINT 18,DMRCRG(W)] ;GET A BYTE POINTER TO "CRASH REGS"
MOVEI T1,4 ;GET COUNT OF REGISTERS TO READ
DMRKL1: RDIO S,(T2) ;GET DMR11 REGISTER
IDPB S,T3 ; AND STORE IT IN THE DMR BLOCK FOR DEBUGGING
ADDI T2,2 ;GO TO THE NEXT -11 WORD
SOJG T1,DMRKL1 ;LOOP OVER ALL REGISTERS
MOVE T1,DMRMAP(W) ;GET THE INITIAL MAP REG FOR THIS DMR
SETZ T2, ;GET A ZERO,
MOVEI T3,DMRPPL ;Pages/line
SOJGE T3,[WRIO T2,(T1);Clear a mapping register
AOJA T1,.] ;Loop over all of em
LDB T1,PDRSTS ;GET THE STATE OF THIS LINE
CAIE T1,DR%HLT ;WAS LINE "ALIVE"
PUSHJ P,DMRPDN ;Tell user protocol down
MOVEI T2,DR%HLT ;GET THE "DOWN" STATE
DPB T2,PDRSTS ; AND MARK THIS LINE AS HOPELESS
POPJ P, ;Done
SUBTTL Protocol Control Subroutines
;DMRPUP - Routine to tell user protocol is up
DMRPUP: AOS DMRLUP(W) ;Count line up event
PUSH P,T4 ;Save T4 for caller
MOVEI T1,KI.PRU ;Function for user
PUSHJ P,CALUSR ;Bring happiness and good cheer
POP P,T4 ;Restore T4
POPJ P, ;Done
;DMRPDN - Routine to tell user protocol is down.
;DMRPD0 - Routine to flush the queues, returning output-not-done
DMRPDN: AOS DMRLDN(W) ;Count line down event
PUSHJ P,DMRPD0 ;Flush the queues
MOVEI T1,KI.PRD ;Function for user
PUSHJ P,CALUSR ;Bring sadness and death
POPJ P, ;Done
DMRPD0: DMROFF ;We're hacking the queues...
DMRPD1: HRRZ T3,DMRWTO(W) ;GET THE FIRST PCB ON THE "WAIT OUTPUT" QUEUE
JUMPE T3,DMRPD2 ; IF NO MSGS AWAITING OUTPUT, CHECK AWAIT ACK
HRRZ T1,MB.NXT(T3) ;GET THE ADDRESS OF THE "SECOND" MESSAGE
HRRZM T1,DMRWTO(W) ; AND MAKE IT BE THE "FIRST"
JRST DMRPD3 ;RELEASE THE MESSAGE
DMRPD2: HRRZ T3,DMRWTA(W) ;GET THE FIRST PCB ON THE "WAIT ACK" QUEUE
JUMPE T3,DMRPD4 ; IF NONE, GO TELL USER THAT LINE IS DOWN
SOS DMRCTA(W) ;COUNT OFF ONE LESS "AWATING ACK"
HRRZ T1,MB.NXT(T3) ;GET THE SECOND PCB ON THE WAIT ACK QUEUE
HRRZM T1,DMRWTA(W) ; AND MAKE IT THE FIRST
DMRPD3: DMRON ;Turn interrupts back on
MOVEI T1,KI.OND ;Say output not done
HLLZS MB.NXT(T3) ;It's not polite to point
PUSHJ P,CALUSR ;Tell user bad news
JRST DMRPD0 ;And keep flushing
DMRPD4: SKIPE DMRCTA(W) ;MAKE SURE WE DIDN'T LOSE ANY MSGS
PUSHJ P,NTDSTP## ;++ ERROR: ACK QUEUE WRONG LENGTH
DMRON ;Interrupts OK now
PJRST FREMAI ;Done, see if MAINT msgs to free
SUBTTL GETBUF - Get a buffer for a message, or starve
;By the time we get around to asking for a buffer, the DMR has alread ACKd
;this message. So we can't really toss it, without damage, especially to
;Phase II DECnet nodes. We gave the DMR a comm page buffer on blind faith,
;which has been shattered. So, we mark the DMR as needing attention at
;once/sec level, and simply starve it. If things are bad enough, in a
;little while, it will start NAKing some other message, since it will have
;no buffers at all. When we can get a buffer at clock level to put the
;current message into, we'll feed the DMR another buffer, and it will
;crank up the line again. If things aren't bad at all, which is the
;typical ANF-10 case (NETSER just didn't get a chance to give us a buffer)
;we'll just continue happily along, and the other end will never know...
GETBUF: MOVEI T1,DMRSTV ;The I'm starving bit
ANDCAM T1,DMRSTS(W) ;Not yet, I'm not
PUSH P,T3 ;Save length
PUSH P,T4 ;and CBP to data
MOVEI T1,KI.BFR ;I need a buffer
PUSHJ P,CALUSR ;Please
POP P,T4
POP P,T3
JUMPN T1,CPOPJ## ;Got one, we lucked out
MOVEI T2,DMRSTV ;Failed, were starving
IORM T2,DMRSTS(W) ;Now
POPJ P, ;We'll try again later
SUBTTL CALUSR - Routine to dispatch to line's user
;CALUSR - Routine to call this line's user
;Call: W := Pointer to this DMR's DMR block
; T1 := Interrupt function (KI.???), T3 := function data
; PUSHJ P,CALUSR
;
;CALUSR sets up T2 with the Line block address (eg FEK, CB, DLX, DDB, ...)
;Users are not allowed to smash F/W!
CALUSR: PUSHJ P,SAVR## ;DECnet trashes R, so save it
HRRZ T2,DMRUSR(W) ;Get line's user code
CAILE T2,DD.MAX ;In range?
PUSHJ P,NTDSTP## ;Die, die, die...!
LSH T2,1 ;Convert to biword offset
PJRST CALDVN(T2) ;Go dispatch
DEFINE X(TYP,FT,BLK,DSP),<
IFN <.-CALDVN-<2*DD.'TYP>>,<PRINTX ? CALUSR vector wrong for TYP>
IFE FT,<
PUSHJ P,NTDSTP## ;;++ User not supported in this monitor!
HALT .-1> ;;Fill out the block
IFN FT,<
MOVE T2,DMR'BLK(W) ;;Point to user's block
PJRST DSP> ;;And dispatch to user's entry vector
>;Define X
CALDVN: X(NOBODY,1,USR,NOBDSP##) ;The null user
X(ANF10,FTNET,FEK,D8RK2U##) ;Real networks
X(DECNET,FTDECN,LBK,RTRDSP##) ;Imitation networks
X(PROGRAM,1,DDP,DMRKTU##) ;UUOCON
X(IBMCOMM,FTDN60,DLX,D6RK2U##) ;Non-networks
IFN <.-CALDVN-<2*<DD.MAX+1>>>,<PRINTX ? CALDVN user entry missing!>
SUBTTL PRINT ROUTINES
;PRDMR - ROUTINE TO PRINT OUT "DMR11 #?"
PRDMR: PUSHJ P,INLMES## ;PRINT OUT THE ASCII PART
ASCIZ /DMR11 #/
HRRZ T1,DMRNUM(W) ;GET THE DMR'S NUMBER
PUSHJ P,PRTDIG## ; AND PRINT THAT
PUSHJ P,INLMES## ;Identify further
ASCIZ /(Synch line /
HLRZ T1,DMRLIN(W) ;Line number on this node
PUSHJ P,PRTDIG## ;Add it
PUSHJ P,INLMES## ;Finish up
ASCIZ /)/
POPJ P,
;ROUTINE TO SAVE F & W FOR THE DMR??? ROUTINES
SAVEFW: EXCH F,(P) ;SAVE F, GET PC
PUSH P,W ;SAVE W
PUSH P,S ;SAVE S TOO...
MOVEM F,1(P) ;PUT PC IN A "SAFE" PLACE
MOVE F,-2(P) ;GET F BACK
PUSHJ P,@1(P) ;CALL CALLER BACK.
SKIPA ;NON SKIP RETURN
AOS -3(P) ;PROPAGATE THE SKIP RETURN
POP P,S ;GET S BACK
POP P,W ;RESTORE W
POP P,F ;RESTORE F
POPJ P, ;RETURN
;ROUTINES TO DO "CANONICAL" BYTE POINTER MANIPULATION
;BECAUSE THE BYTES TO/FROM THE UBA ARE SWAPPED NORMAL PDP-10 BYTE
; INSTRUCTIONS DON'T WORK. HENCE THE "CANONICAL" -11 BYTE POINTER
; AND THESE ROUTINES.
;THE FORMAT OF THE BYTE POINTER IS:
;
; XWD BYTE-POS,ADDR
;
;WHERE
;
; BYTE-POS = {0, 1, 2, 3} ;THE POSITION OF THE BYTE IN THE -10 WORD
; ADDR = THE 10 ADDRESS OF THE WHOLE WORD
;
;HERE IS A TABLE OF BYTE POINTERS TO THE FOUR -11 BYTES IN A -10 WORD
BYTABL: POINT 8,(T4),17 ;FIRST BYTE
POINT 8,(T4),9 ;SECOND BYTE
POINT 8,(T4),35 ;THIRD BYTE
POINT 8,(T4),27 ;FOURTH BYTE
;IDPCB - ROUTINE TO INCREMENT AND DEPOSITE CANONICAL BYTE
;CALL MOVEI S,BYTE ;THE BYTE TO STORE
; MOVE T4,BP ;A CANONICAL BYTE POINTER AS ABOVE
; PUSHJ P,IDPCB ;DO THE DEPOSIT BYTE FUNCTION
;
;CLOBBERS NO REGISTERS
IDPCB: PUSH P,T1 ;GET A TEMP
HLRZ T1,T4 ;GET THE POSITION FIELD
AOS T1 ;INCREMENT THE POSITION
CAIL T1,4 ;SEE IF WE HAVE GONE TO NEXT WORD YET
JRST [AOS T4 ;IF NEXT WORD, INCREMENT ADDRESS
SETZ T1, ; AND SET BYTE POSITION TO ZERO
JRST .+1] ;BACK TO MAIN FLOW
HRLM T1,T4 ;PUT THE UPDATED POSITION BACK
DPB S,BYTABL(T1) ;STORE THE BYTE
POP P,T1 ;RESTORE T1
POPJ P, ;RETURN (NOT "JRST TPOPJ" TO SAVE TIME)
;ILDCB - ROUTINE TO INCREMENT AND LOAD CANONICAL BYTE
;CALL MOVE T4,BP ;A CANONICAL BYTE POINTER AS ABOVE
; PUSHJ P,IDPCB ;DO THE DEPOSIT BYTE FUNCTION
;RETURN CPOPJ ; S := REQUESTED BYTE
;CLOBBERS NO REGISTERS (EXCEPT S)
ILDCB: PUSH P,T1 ;GET A TEMP
HLRZ T1,T4 ;GET THE POSITION FIELD
AOS T1 ;INCREMENT THE POSITION
CAIL T1,4 ;SEE IF WE HAVE GONE TO NEXT WORD YET
JRST [AOS T4 ;IF NEXT WORD, INCREMENT ADDRESS
SETZ T1, ; AND SET BYTE POSITION TO ZERO
JRST .+1] ;BACK TO MAIN FLOW
HRLM T1,T4 ;PUT THE UPDATED POSITION BACK
LDB S,BYTABL(T1) ;FETCH THE BYTE
POP P,T1 ;RESTORE T1
POPJ P, ;RETURN (NOT "JRST TPOPJ" TO SAVE TIME)
;DLDCB - ROUTINE TO DECREMENT AND LOAD CANONICAL BYTE
;CALL MOVE T4,BP ;A CANONICAL BYTE POINTER
; PUSHJ P,DLDCB ;DECREMENT AND LOAD BYTE
;RETURN CPOPJ ;WITH S := THE BYTE
;
;CLOBBERS NO REGISTERS (EXCEPT "S" OF COURSE)
DLDCB: HLRZ S,T4 ;GET THE BYTE "POSITION"
SOJL S,[SOS T4 ;DECREMENT POSITION AND ADDR IF NECESSARY
MOVEI S,3 ;IF BACKING UP, SET BP TO 4TH BYTE
JRST .+1] ;BACK TO MAIN STREAM
HRLM S,T4 ;PUT THE "POSITION" BACK IN THE BP
ADDI S,BYTABL ;INDEXING DOESN'T WORK FOR "S"
LDB S,@S ;GET THE REQUESTED BYTE
POPJ P, ;RETURN
DMREND::END