TITLE D8RINT - SERVICE FOR DMR11 V022 SUBTTL T. LITT 15 DEC 87 ;From D8KINT V026 SEARCH F, S ,NETPRM, D36PAR $RELOC $HIGH ;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED ; OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE. ; ;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1981,1984,1986,1988. ;ALL RIGHTS RESERVED. .CPYRT<1981,1988> XP VD8RINT,022 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,DC.FQB ;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,DC.IOF ;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,DC.FSM ;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,DC.FQB ;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,DC.IOF ;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,DC.IPU ;FIRST RANGE CHECK THE CAILE T1,DC.ICC ; 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,DC.FIL ;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>),-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,+DVC2IO,DMRMMS+1> ;BUFFER SIZE X DEVSER, ;DEFINE NETWORK DISPATCH VECTOR X DEVMOD, X DEVSTA,,DEPEVM> ;VARIABLE BUFFERS, NO EVM ; X DEVCPU, ;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,_<-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,DC.FHL ;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, 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 ; 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 ;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 ;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 ;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 ; 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 ;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 ;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 ;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,DC.FQB ;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,DC.IOF ;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 ;GET THE ADDRESS OF THE NEXT USER'S BUFFER EXCTUX ;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,DC.FIL ;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,DC.FSM ;WE WANT TO PUT IN MAINT STATE DMRCK2: PUSH P,T1 ;SAVE THE "DESTINATION STATE" MOVEI T1,DC.FHL ;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,DC.IPU ;RANGE CHECK THE FUNCTION CODE CAILE T1,DC.ICC ; 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,+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,+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,+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 DC.F??. 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 DC.I??. 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*PAGSIZ ;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==</<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 , EBFOU0==EBFIN0+ ;11 Address of Output Buffer 0 TBFOU0==TBFIN0+ ;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-> ;10 Words wasted at end of Comm region IFL WASTE, ;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 HLRZ T1,DMRCSR(W) ;GET UNIBUS ADAPTER NUMBER MOVEI T2,DMRPPL ;NUMBER OF MAPPING REGISTERS PUSHJ P,AUTAMR## ;ALLOCATE UNIBUS MAPPING REGISTERS JRST DMRNXP ;NO GOOD MOVEM T1,DMRMAP(W) ;SAVE INITIAL MAPPING REGISTER MOVEM T3,DMREAD(W) ;SAVE INITIAL ELEVEN ADDRESS 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 (DC.F??) ; MOVX T2,DMR-BLOCK ADDRESS ; MOVX T3,BUFFER ADDRESS OR PARAMETER # (ONLY FOR DC.FQB/NTMAN) ; MOVX T4,PARAMETERVALUE (ONLY DC.CSC) ; 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,DC.FAL ; FUNCTION CODE [DC.FMX] PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR CAIN T1,DC.FAL ;ASSIGN LINE? JRST KF.AL ;YES 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,DC.FQB ; FUNCTION CODE [DC.FMX] PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR CAIN T1,DC.FAL ;ASSIGN LINE? JRST KF.AL ;YES 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,DC.FQB ; FUNCTION CODE [DC.FMX] PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR CAIN T1,DC.FAL ;ASSIGN LINE? JRST KF.AL ;YES 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,DC.FQB ;FUNCTION CODE [DC.FMX] PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP PUSHJ P,SAVEFW ;WE USE F := COMM, W := DMR CAIN T1,DC.FAL ;ASSIGN LINE? JRST KF.AL ;YES 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 NTDSTP## ;4 = ASSIGN -- SEE SPECIAL DECODE ABOVE IFIW KF.SE ;5 = SET \ IFIW KF.RE ;6 = READ ) NETWORK MANAGEMENT IFIW KF.CL ;7 = CLEAR/ ;KF.AL - ROUTINE TO ASSIGN A LINE ;CALL MOVX T2,DECNET LINE ID KF.AL: LDB W,[POINT 9,T2,17] ;YES, CONVERT LINE ID (LIKON) CAIGE W,M.DMRN## ;BETTER BE IN RANGE SKIPN W,DMRTBL##(W) ;POINT TO THE DMR BLOCK POPJ P, ;PATCHED OUT HRRZ T2,DMRUSR(W) ;GET LINE OWNER CAIE T2,DD.DEC ;HACKING? POPJ P, ;NOT ME, YOU DON'T MOVE T1,DMRCSR(W) ;GET CSR ADDRESS PUSHJ P,UBGOOD## ;MAKE SURE IT'S THERE POPJ P, ;IT'S NOT, CAN'T ASSIGN MOVEM T3,DMRLBK(W) ;SAVE ADDRESS OF DECNET'S BLOCK MOVE T1,W ;RETURN ADDRESS OF OURS JRST CPOPJ1## ;SUCCESS ;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,DC.IOF ; 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,DC.IOD ;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,DC.IMR ;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,DC.IIC ;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: MOVE T2,PCBCTR(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 MOVEM T3,PCBCTR(T1) ;RETURN THE LENGTH TO NETSER MOVE T2,PCBPTR(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,DC.IIC ;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 PCBPT2 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) MOVE T1,PCBCTR(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,PCBPTR(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,PCBPTR(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 PCBCT2(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,PCBPT2(U) ;GET THE BYTE POINTER FOR IT MOVE T0,PCBCT2(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: MOVE T1,PCBCT2(U) ;GET COUNT OF SECONDARY BUFFER MOVE T2,PCBPT2(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,PCBCT2(U) ;GET THE COUNT BACK IMULI T1,^D12 ; (12 * PCBCTR) + 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: MOVE T1,PCBCT2(U) ;GET COUNT OF SECONDARY DATA MOVE T2,PCBPT2(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 MOVE T1,DMRCSR(W) ;Get Unibus address PUSHJ P,UBGOOD## ;Webe good? POPJ P, ;No, don't even try 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 HLRZ T1,DMRCSR(W) ;Get UNIBUS adapter number MOVE T2,DMRVEC(W) ;Get vector address for this DMR PUSHJ P,AUTVIA## ;Compute address of interrupt instruction MOVSI T2,(XPCW) ;Interrupt instruction is an XPCW 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,DC.IOC ;INTERRUPT DRIVER TO CREATE CIRCUIT MOVE T3,W ;GET POINTER TO KONTROLLER LINE BLOCK HRRZ T4,DMRNUM(W) ;GET OUR DMR NUMBER MOVSI T4,(T4) ;KONTROLLER TYPE + KON # DNCALL DMIPPI## ;TELL ROUTER ABOUT THIS LINE SKIPN T1 ;WHERE WE SUPPLIED A CIRCUIT BY ROUTER? PUSHJ P,NTDSTP## ;NOPE, WE'LL HAVE TO DIE 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,DC.IOD ;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,DC.IPU ;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,DC.IPD ;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,DC.IOF ;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,DC.IGB ;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>>, 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,CALPPI) ;Imitation networks X(PROGRAM,1,DDP,DMRKTU##) ;UUOCON X(IBMCOMM,1,DLX,D6RK2U##) ;Non-networks IFN <.-CALDVN-<2*>>, IFN FTDECN,< CALPPI: DNCALL DMIPPI## ;CALL DECNET IN DECNET CONTEXT POPJ P, ;RETURN PJRST CPOPJ1## ;... >; END IFN FTDECN 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