mirror of
https://github.com/PDP-10/stacken.git
synced 2026-02-13 11:24:09 +00:00
2725 lines
102 KiB
Plaintext
2725 lines
102 KiB
Plaintext
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>),<NAME==:<%%%OFF==%%%OFF+SIZE>-SIZE>
|
||
|
||
%%%OFF==DEVLLD ;INITIALIZE THE OFFSET
|
||
|
||
X DEVDMR ;XWD DMR-ADDRESS, INPUT MSG QUEUE
|
||
X DEVOBC ;COUNT OF OUTPUT BUFFERS OUTSTANDING
|
||
X DMRDBL,0 ;LENGTH OF A DMR DDB
|
||
|
||
PURGE X ;FLUSH THE MACRO
|
||
|
||
|
||
;NOW LAY OUT THE PROTOTYPE DDB
|
||
|
||
DEFINE X(OFFSET,EXPR)< ;MACRO TO SET SELECTED LOCATIONS IN THE BLOCK
|
||
RELOC DMRDDB+OFFSET ;GO TO THE RIGHT WORD
|
||
EXPR ;ASSEMBLE IN THE EXPRESSION
|
||
>
|
||
$LOW ;WE NEED TO LINK THIS IN
|
||
DMRDDB::X DEVCHR,<XWD <6*HUNGST>+DVC2IO,DMRMMS+1> ;BUFFER SIZE
|
||
X DEVSER,<XWD 0,DMRUDS> ;DEFINE NETWORK DISPATCH VECTOR
|
||
X DEVMOD,<XWD DVIN!DVOUT,1_BYTMOD>
|
||
X DEVSTA,<XWD DEPLEN!<.TYDMR*.TYEST>,DEPEVM> ;VARIABLE BUFFERS, NO EVM
|
||
; X DEVCPU,<EXP 707B8> ;SET DEYPCL SO WILL RUN ON ANY CPU.
|
||
X NETLEN,0 ;RESERVE ENOUGH SPACE FOR ENTIRE DDB
|
||
|
||
$HIGH ;BACK TO PURE CODE
|
||
PURGE X ;FLUSH THE MACRO
|
||
SUBTTL DMRUUO -- UUO ENTRY TO DMRSRV
|
||
|
||
;DISPATCH TABLE (FROM UUOCON)
|
||
|
||
JRST CPOPJ## ;(-5) DEVICE OFF LINE
|
||
JRST CPOPJ## ;(-4) SPECIAL ERROR STATUS
|
||
JRST REGSIZ## ;(-3) LENGTH CAN BE GOTTEN FROM DDB
|
||
JRST CPOPJ## ;(-2) INITIALIZE (WE DO IT IN TSTDMR)
|
||
JRST CPOPJ## ;(-1) HUNG DEVICE
|
||
DMRUDS: JRST DMRREL ;(0) RELEASE (MAKE SURE DMR DOESN'T POINT)
|
||
JRST DMRCLO ;(1) CLOSE OUTPUT
|
||
JRST DMROUT ;(2) OUTPUT
|
||
JRST DMRIN ;(3) INPUT
|
||
;TSTDMR ROUTINE CALLED FROM UUOCON DURING A PHYSICAL DEVICE SEARCH
|
||
;CALL T1 := DEVICE NAME
|
||
;RETURN CPOPJ NOT DMR, NOT IN OPEN UUO, ALREADY ASSIGNED OR NO PRIVS
|
||
; CPOPJ1 F := DDB ADDRESS
|
||
|
||
TSTDMR::HLRZ T2,T1 ;GET THE LH OF THE NAME,
|
||
TRNN T1,007777 ;IF NOT A SINGLE DIGIT UNIT NUMBER
|
||
CAIE T2,'DMR' ; OR IF IT ISN'T "DMR"
|
||
POPJ P, ; THEN GIVE THE ERROR RETURN RIGHT AWAY
|
||
LDB T2,[POINT 9,M,8] ;GET THE OP-CODE OF THE UUO THAT GOT HERE
|
||
CAIE T2,<OPEN>_<-33> ;DON'T ALLOW RANDOM COMMANDS TO BUILD DDB'S
|
||
POPJ P, ;IF NOT AN OPEN UUO, THEN DEVICE NOT THERE
|
||
PUSHJ P,SAVT## ;SAVE THE TEAS
|
||
PUSHJ P,SAVE1## ; AND A PEA
|
||
MOVE P1,T1 ;STORE THE DEVICE NAME IN P1 FOR THE DURATION
|
||
MOVSI T1,JP.POK ;ONE LAST QUICK CHECK, DON'T ALLOW
|
||
PUSHJ P,PRVBIT## ; JUST ANYONE TO USE THE DEVICE.
|
||
CAIA ; ONLY OPR AND POKE ALLOWED.
|
||
POPJ P, ;STUPID BASS AKWARDS UUO
|
||
|
||
;NOW CHECK THE NAME
|
||
LDB T1,DRNDMR ;GET THE CONTROLLER NUMBER
|
||
SUBI T1,'0' ;Make it binary
|
||
SKIPGE T1 ;IF LEGAL
|
||
POPJ P, ;BAD
|
||
CAIG T1,M.DMRN##-1 ; AND MAKE SURE IT'S BOTH LEGAL
|
||
SKIPN W,DMRTBL##(T1) ; AND THAT IT EXISTS
|
||
POPJ P, ;IF IT DOESN'T EXIST, USER CAN'T OPEN IT
|
||
SKIPE DMRDDP(W) ;SEE IF SOMEONE ELSE OWN'S THE DDB
|
||
JRST CLRFPJ ; IF SO, ZERO F AND RETURN
|
||
|
||
;NOW BUILD A DDB
|
||
MOVEI T2,DMRDBL ;GET THE LENGTH OF A DMR DDB
|
||
PUSHJ P,GETWDS## ; AND ALLOCATE THE SPACE
|
||
JRST CLRFPJ ; IF NOT ENOUGH, GIVE THE FAIL RETURN
|
||
MOVSI T2,DMRDDB ;GET THE ADDRESS OF THE PROTOTYPE
|
||
HRR T2,T1 ; AND A COPY OF THE ADDRESS OF THE NEW ONE
|
||
DDBSRL ;LOCK THE DDB CHAIN
|
||
BLT T2,DMRDBL-1(T1) ;INITIALIZE THE NEW ONE
|
||
HRLM T1,DEVSER+DMRDDB ; AND SET UP THE LINKS
|
||
DDBSRU ;DDB IS IN PLACE, UNLOCK THE DDB CHAIN
|
||
HRLM F,DEVDMR(T1) ;MAKE THE DDB POINT TO THE DMR BLOCK
|
||
EXCH T1,DMRDDP(W) ;MAKE THE DMR BLOCK POINT TO THE DDB
|
||
SKIPE T1 ;JUST A PARANOID CHECK.
|
||
PUSHJ P,NTDSTP## ; I DON'T THINK THIS IS POSSIBLE
|
||
MOVE F,DMRDDP(W) ;GET THE ADDRESS OF THE DDB
|
||
MOVEM P1,DEVNAM(F) ;SET THE DEVICE'S NAME
|
||
RETSKP ; AND GIVE A GOOD RETURN TO DDBSRC
|
||
|
||
CLRFPJ: SETZ F, ;CLEAR "F" IF AN ERROR
|
||
POPJ P, ; AND RETURN
|
||
;DMRREL ROUTINE TO PROCESS THE "RELEASE" FUNCTION OF A DMR
|
||
;CALL F := ADDRESS OF THE DDB
|
||
;RETURN CPOPJ ;ALWAYS
|
||
|
||
DMRREL: PUSHJ P,DMRCKO ;SEE IF WE ARE THE LINE'S "OWNER"
|
||
JRST DMRRL1 ;IF NOT, DON'T MESS WITH THE KONTROLLER
|
||
MOVEI T1,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,<PRINTX ?DMRSER assumes that ACs M and T6 are the same
|
||
PRINTX ?Because it knows that M is OK to trash>
|
||
|
||
DMRIN: PUSHJ P,SAVE2## ;P1 = DEVIAD, P2 = INPUT MESSAGE
|
||
MOVSI S,IOBEG!IO ;CLEAR IOBEG SET "INPUT"
|
||
ANDCAB S,DEVIOS(F) ; FOR NO PARTICULARLY GOOD REASON
|
||
PUSHJ P,DMRCKM ;MAKE SURE WE'RE IN THE CORRECT MODE
|
||
|
||
KILOOP: PUSHJ P,DMRONL ;MAKE SURE THE DMR IS UP
|
||
POPJ P, ; IF NOT, RETURN WITH ERROR BITS SET
|
||
HRRZ P1,DEVIAD(F) ;GET THE ADDRESS OF THE INPUT BUFFER
|
||
SKIPE T1,P1 ;MAKE SURE THAT THERE IS ONE,
|
||
EXCTUX <SKIPGE (T1)> ; AND THAT IT IS EMPTY
|
||
POPJ P, ;IF NO EMPTY BUFFER, THEN RETURN
|
||
PUSHJ P,BRNGE## ;MAKE SURE THE BUFFER IS ADDRESSABLE
|
||
HRRZ P2,DEVDMR(F) ;GET THE ADDRESS OF THE INPUT MESSAGE
|
||
JUMPE P2,KIWAIT ;IF NO INPUT, GO WAIT FOR SOME
|
||
|
||
;NOW SET UP TO COPY THE DATA
|
||
EXCTUX <HLRZ T4,(P1)> ;GET THE LENGTH OF THE INPUT BUFFER (+1)
|
||
SUBI T4,1 ;GET ACTUAL LENGTH IN WORDS
|
||
LSH T4,2 ; AND CONVERT THAT TO BYTES
|
||
MOVSI T5,(POINT 8) ;MAKE A BYTE POINTER TO THE
|
||
HRRI T5,2(P1) ; USER'S INPUT BUFFER
|
||
MOVE T3,MB.FMS(P2) ;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
|
||
MOVE T1,MD.BYT(T3) ;FROM THE MSC, GET THE BYTE COUNT
|
||
MOVE T6,MD.ALA(T3) ;BYTE POINTER IS INDEXED BY T6
|
||
MOVE T2,MD.PTR(T3) ; AND THE BYTE POINTER
|
||
CAMGE T4,T1 ;MAKE SURE THAT THE DATA WILL FIT,
|
||
JRST K.BKTL ; IF NOT, THEN GIVE "BLOCK TOO LARGE"
|
||
MOVEI T4,3(T1) ;MAKE THE "DST" LENGTH BE THE SOURCE
|
||
TRZ T4,3 ; LENGTH ROUNDED UP SO AS TO ZERO FILL LAST WD
|
||
EXCTUU <HRRM T1,1(P1)> ;STORE THE BYTE COUNT FOR THE USER
|
||
IFE DALBUG,< ;D.A.LEWINE
|
||
PXCT 1,[EXTEND T1,[EXP MOVSLJ,0]] ;AND COPY THE DATA
|
||
PUSHJ P,NTDSTP## ;WE CHECKED... THIS SHOULDN'T HAPPEN
|
||
>
|
||
IFN DALBUG,< ;UNTIL THEY GET THE MICRO-CODE RIGHT...
|
||
JRST .+3 ;SKIP INTO THE MIDDLE OF THE LOOP
|
||
ILDB T4,T2 ;GET THE NEXT BYTE
|
||
EXCTUU <IDPB T4,T5> ;STORE THE NEXT BYTE
|
||
SOJGE T1,.-2 ;LOOP TILL ALL STORED
|
||
SETZ T3, ;CLEAR THE "TO COUNT"
|
||
>
|
||
;NOW DEQUEUE THE INPUT MESSAGE AND ADVANCE THE USER'S BUFFER
|
||
DMROFF ;KEEP INTERRUPT LEVEL OUT
|
||
HRRZ T1,MB.NXT(P2) ;GET THE ADDRESS OF THE NEXT MESSAGE
|
||
HRRM T1,DEVDMR(F) ; AND MAKE THAT ONE BE THE FIRST
|
||
DMRON ;RE-ENABLE INTERRUPTS
|
||
MOVE T1,P2 ;GET THE DRB ADDR BACK
|
||
PUSHJ P,GIVDRB ;FREE THE MESSAGE BLOCK
|
||
PUSHJ P,ADVBFF## ;ADVANCE THE USER'S INPUT BUFFER
|
||
POPJ P, ;IF WE'RE SUPPOSED TO STOP, RETURN TO UUOCON
|
||
JRST KILOOP ;OTHERWISE TRY TO DO MORE INPUT
|
||
;DMROUT ROUTINE TO PROCESS THE OUTPUT UUO FOR THE DMR DEVICE
|
||
;CALL F := DDB ADDRESS
|
||
;RETURN CPOPJ ;ALWAYS
|
||
DMRCLO:
|
||
DMROUT: PUSHJ P,SAVE2## ;P1 := DEVOAD, P2 := DATA BUFFER ADDRESS
|
||
MOVSI S,IOBEG ;CLEAR IOBEG
|
||
ANDCAB S,DEVIOS(F) ; FOR NO PARTICULAR GOOD REASON
|
||
MOVSI S,IO ;GET AND SET
|
||
IORB S,DEVIOS(F) ; "OUTPUT" SO IOSETC WORKS RIGHT
|
||
PUSHJ P,DMRCKM ;CHECK THE MODE
|
||
|
||
KOLOOP: PUSHJ P,DMRONL ;MAKE SURE THAT THE DEVICE IS ONLINE
|
||
POPJ P, ;IF NOT, RETURN (WITH ERROR BITS SET)
|
||
HRRZ P1,DEVOAD(F) ;GET THE ADDRESS OF THE OUTPUT BUFFER
|
||
SKIPE T1,P1 ;MAKE SURE THAT THERE IS ONE, AND
|
||
EXCTUX <SKIPL (T1)> ; THAT IT HAS DATA IN IT
|
||
POPJ P, ;IF NO FULL BUFFER, THEN EXIT
|
||
PUSHJ P,BRNGE## ;MAKE SURE THE BUFFER IS ADDRESSABLE
|
||
|
||
AOS T1,DEVOBC(F) ;COUNT UP THE NUMBER OF BUFFERS OUTSTANDING
|
||
CAILE T1,DMRMXQ ;IF TOO MANY, THEN
|
||
JRST [SOS DEVOBC(F) ; TAKE BACK WHAT WE JUST SAID
|
||
JRST KOWAIT] ; AND WAIT FOR SOME TO GET SENT
|
||
|
||
;NOW ALLOCATE A DMR DATA BLOCK TO HOLD THE DATA
|
||
KOLOO1: EXCTUX <HRRZ T1,1(P1)> ;GET THE NUMBER OF USER BYTES
|
||
CAILE T1,DMRMMS*4 ;MAKE SURE THAT THE NUMBER IS REALISTIC
|
||
JRST [SOS DEVOBC(F) ;IF TOO MANY, TAKE BACK THE COUNT
|
||
JRST K.BKTL] ; AND TELL THE USER "BLOCK TOO LARGE"
|
||
PUSHJ P,GETDRB ;GET A BUFFER FOR THE MESSAGE
|
||
JRST [SOS DEVOBC(F) ;SINCE ^C CAN HAPPEN HERE...
|
||
MOVEI T1,2 ;IF NO CORE AVAILABLE,
|
||
PUSHJ P,SLEEPF## ;SLEEP FOR 2 SECONDS AND
|
||
JRST KOLOOP] ; TRY AGAIN
|
||
MOVE P2,T1 ;REMEMBER THE ADDRESS OF THE MESSAGE BLOCK
|
||
;NOW COPY THE DATA INTO THE DATA BLOCK
|
||
EXCTUX <HRRZ T1,1(P1)> ;GET THE USER'S BYTE COUNT BACK AGAIN
|
||
MOVSI T2,(POINT 8) ;BUILD A BYTE POINTER TO THE
|
||
HRRI T2,2(P1) ; USER'S DATA
|
||
MOVE T3,MB.FMS(P2) ;GET THE ADDRESS OF THE SEGMENT DESCRIPTOR
|
||
MOVE T4,T1 ;GET THE LENGTH
|
||
MOVEM T1,MD.BYT(T3) ;STORE THE NUMBER OF BYTES WE'RE GOING TO COPY
|
||
MOVE T5,MD.AUX(T3) ;GET THE ADDRESS OF THE FIRST BYTE
|
||
MOVE T6,MD.ALA(T3) ;MD.AUX IS INDEXED BY T6
|
||
IFE DALBUG,< ;D.A.LEWINE
|
||
PXCT 2,[EXTEND T1,[EXP MOVSLJ,0]] ;COPY THE DATA
|
||
PUSHJ P,NTDSTP## ;CAN'T HAPPEN
|
||
>
|
||
IFN DALBUG,< ;UNTIL THEY GET THE MICRO-CODE RIGHT...
|
||
JRST .+3 ;SKIP INTO THE MIDDLE OF THE LOOP
|
||
EXCTUX <ILDB T4,T2> ;LOAD THE NEXT BYTE
|
||
IDPB T4,T5 ; AND STORE IT IN THE MONITOR BUFFER
|
||
SOJGE T1,.-2 ;LOOP TILL ALL STORED
|
||
SETZ T3, ;CLEAR THE "TO COUNT"
|
||
>
|
||
MOVEI T1,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 <HRRZ T1,(T1)> ;GET THE ADDRESS OF THE NEXT USER'S BUFFER
|
||
EXCTUX <SKIPL (T1)> ;IF HE HAS INPUT TO READ,
|
||
TRNE T2,DEPAIO ; OF IF THIS IS ASYNCH IO, THEN
|
||
POPJ P, ; RETURN TO THE USER
|
||
MOVEI T1,EV.DMR ;OTHERWISE, GO INTO
|
||
PUSHJ P,ESLEEP## ; EVENT WAIT AND THEN
|
||
JRST KILOOP ; CHECK FOR MORE INPUT
|
||
|
||
|
||
;KOWAIT WAIT FOR OUTPUT TO COMPLETE
|
||
KOWAIT: MOVE T1,DEVAIO(F) ;GET THE WORD WITH THE ASYNCH IO BITS
|
||
TRNE T1,DEPAIO ;SEE IF WE ARE DOING ASYNCH IO
|
||
POPJ P, ;IF ASYNCH IO, THEN RETURN TO THE USER
|
||
MOVEI T1,EV.DMR ;OTHERWISE, GO INTO
|
||
PUSHJ P,ESLEEP## ; EVENT WAIT, AND
|
||
JRST KOLOOP ; THEN SEE IF WE CAN DO OUTPUT
|
||
|
||
|
||
;DMRIAV ROUTINE TO SIGNAL THAT DMR INPUT IS AVAILABLE
|
||
DMRIAV: MOVE T1,DEVAIO(F) ;GET THE WORD WITH THE ASYNCH IO BITS
|
||
TRNN T1,DEPAIO ; AND IF THIS IS NOT ASYNCH IO,
|
||
JRST [LDB T1,PJOBN## ; ASSUME THAT THE GUY IS IN EVENT WAIT
|
||
PJRST EWAKE##] ; AND GO WAKE HIM
|
||
PUSH P,DEVIOS(F) ;IF ASYNCH IO, THEN MAKE
|
||
MOVSI S,IO ; SURE THAT THE "IO" BIT IS
|
||
ANDCAB S,DEVIOS(F) ; CLEAR. THIS INSURES THAT SETIOD
|
||
PUSHJ P,SETIOD## ; WILL GENERATE AN INPUT DONE INTERRUPT
|
||
POP P,DEVIOS(F) ;RESTORE THE STATE OF DEVIOS
|
||
POPJ P, ; AND RETURN.
|
||
|
||
|
||
;DMROAV ROUTINE TO SIGNAL THAT OUTPUT HAS BEEN COMPLETED
|
||
DMROAV: MOVE T1,DEVAIO(F) ;GET THE ASYNCH IO BITS
|
||
TRNN T1,DEPAIO ;IF WE ARE NOT DOING ASYNCH IO
|
||
PJRST [LDB T1,PJOBN## ; THEN ASSUME THAT THE GUY IS IN
|
||
JRST EWAKE##] ; EVENT WAIT AND WAKE HIM
|
||
PUSH P,DEVIOS(F) ;IF ASYNCH IO, THEN MAKE SURE THAT
|
||
MOVSI S,IO ; THE "IO" BIT SAYS OUTPUT. THIS
|
||
IORB S,DEVIOS(F) ; WILL CAUSE SETIOD TO GENERATE
|
||
PUSHJ P,SETIOD## ; AN "OUTPUT DONE" INTERRUPT.
|
||
POP P,DEVIOS(F) ;RESTORE DEVIOS AND
|
||
POPJ P, ; RETURN
|
||
;DMRCKO ROUTINE TO CHECK OWNERSHIP OF THE DMR BLOCK.
|
||
;CALL F := DDB ADDRESS
|
||
;RETURN CPOPJ ;DMR IS NOT IN "PROGRAM" MODE
|
||
; CPOPJ1 ;DMR IS IN "PROGRAM" MODE
|
||
|
||
DMRCKO: HLRZ T1,DEVDMR(F) ;GET THE DMR PAGE ADDRESS
|
||
HRRZ T1,DMRUSR(T1) ;GET THE USER CODE
|
||
CAIN T1,DD.PRO ;IF IT IS "PROGRAM"
|
||
AOS (P) ; THEN GIVE A GOOD
|
||
POPJ P, ; RETURN
|
||
|
||
|
||
;DMRONL ROUTINE TO CHECK TO SEE IF THE DMR IN "ON LINE"
|
||
;CALL F := DDB ADDRESS
|
||
;RETURN CPOPJ ;NOT OWNED OR NOT ONLINE (ERROR BITS SET)
|
||
; CPOPJ1 ;DMR APPEARS TO BE READY FOR I/O
|
||
|
||
DMRONL: PUSHJ P,DMRCKO ;FIRST SEE IF WE OWN THE LINE
|
||
JRST K.IMPM ; IF NOT, THEN IT'S IMPROPER MODE
|
||
HLRZ T1,DEVDMR(F) ;GET THE DMR BLOCK ADDRESS
|
||
EXCH W,T1 ;PUT ADDRESS IN RIGHT PLACE
|
||
LDB T2,PDRSTS## ;GET CURRENT STATE
|
||
EXCH W,T1 ;THUS
|
||
CAIGE T2,DR%MAI ;MUST BE IN AT LEAST MAINT STATE
|
||
JRST K.DERR ;IF NOT, SAY DEVICE ERROR
|
||
MOVE S,DEVIOS(F) ;FIRST GET THE DEVICE STATUS
|
||
TRNE S,IOSSRM!IOSMRN ; AND IF EITHER ERROR IS STILL LIT
|
||
JRST K.DERR ; RETURN "DEVICE ERROR"
|
||
TRNE S,IODERR!IODTER!IOIMPM!IOBKTL ;IF ANY "STANDARD" ERROR
|
||
POPJ P, ; FORCE THE USER TO CLEARR IT
|
||
TRNE S,IOSMAI ;IF WE ARE TRYING MAINT MODE,
|
||
JRST DMRON1 ; GO CHECK ONLINE DIFFERENTLY
|
||
CAIGE T2,DR%WT1 ;IF SUPPOSED TO BE NORMAL, BUT NOT
|
||
JRST K.DERR ; THEN WE MUST HAVE SCREWED UP
|
||
CAIE T2,DR%RUN ;IF WE'RE RUNNING
|
||
CAIN T2,DR%WT1 ;OR IF WE NEED A MESSAGE TO START
|
||
CAIA ;WE ARE OK
|
||
JRST K.DTER ; ELSE IT'S A "SOFT" ERROR. (WAIT FOR ONLINE)
|
||
RETSKP ;IF IN RUN, THEN ALL'S OK
|
||
|
||
DMRON1: CAIE T2,DR%MAI ;IF WE'RE NOT IN MAINT MODE, THEN
|
||
JRST K.DERR ; IT'S AN ERROR
|
||
RETSKP ;OTHERWISE WE'RE "ONLINE"
|
||
;DMRCKM ROUTINE TO CHECK/SET THE MAINT/NORMAL MODE OF THE LINE
|
||
;CALL F := DDB
|
||
;RETURN CPOPJ ;ALWAYS (STARTS THE LINE IN MAINT/NORMAL)
|
||
|
||
DMRCKM: PUSHJ P,DMRCKO ;SEE IF WE ARE THE LINE'S OWNER
|
||
POPJ P, ; IF NOT, DON'T DO ANYTHING
|
||
PUSH P,W ;FIRST PRESERVE THE DDB POINTER
|
||
HLRZ W,DEVDMR(F) ;FROM THAT GET THE DMR BLOCK ADDRESS
|
||
LDB T1,PDRSTS## ;FROM THAT, GET THE STATE
|
||
POP P,W ;NOW RESTORE THE DDB ADDRESS
|
||
MOVE S,DEVIOS(F) ;RELOAD THE IO STATUS
|
||
TRNE S,IOSMAI ;IF WE'RE SUPPOSED TO BE IN MAINT
|
||
JRST DMRCK1 ; THEN GO CHECK DIFFERENTLY
|
||
CAIL T1,DR%WT1 ;IF WE'RE TRYING TO START OR BETTER
|
||
POPJ P, ; THEN ALL'S OK
|
||
MOVEI T1,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,<DMRHDL*4>+3+4(T1) ;ALLOW FOR OVERHEAD AND ROUND UP
|
||
LSH T2,-2 ;CONVERT TO WORD COUNT
|
||
PUSHJ P,GETWDS## ; AND TRY TO ALLOCATE SPACE
|
||
JRST TPOPJ## ;IF NO CORE, GIVE ERROR RETURN
|
||
POP P,T2 ;GET BYTE COUNT BACK
|
||
SETZM MB.NXT(T1) ;NO POINTERS PLEASE
|
||
SETZM DMRMSD+MD.NXT(T1) ;MAKE SURE THERE IS ONLY 1 MSD
|
||
MOVEM T2,DMRMSD+MD.BYT(T1) ; AND STORE THAT FOR USER
|
||
MOVEM T2,DMRMSD+MD.ALA(T1) ; & PUT IT IN "ALLOC INFO" AREA
|
||
MOVEM T2,DMRMSD+MD.ALL(T1) ; ...
|
||
MOVSI T3,(POINT 8) ;MAKE A BYTE POINTER TO DATA
|
||
HRRI T3,DMRMSD+MD.LEN(T1) ; AREA AFTER HEADER
|
||
MOVEM T3,DMRMSD+MD.PTR(T1) ; AND STORE THAT
|
||
MOVEM T3,DMRMSD+MD.AUX(T1) ;STORE OUTPUT BYTE POINTER ALSO
|
||
MOVEI T3,DMRMSD(T1) ;GET ADDRESS OF MSD
|
||
MOVEM T3,MB.FMS(T1) ; AND STORE IT IN MESSAGE BLOCK
|
||
MOVEI T2,<DMRHDL*4>+3+4(T2) ;GET BYTE COUNT AGAIN
|
||
LSH T2,-2 ; AND CONVERT TO LENGTH OF BLOCK
|
||
ADD T2,T1 ;RELOCATE BY ADDRESS OF BLOCK
|
||
MOVE T3,[SIXBIT /DMRRMD/] ;GET CHECK DATA
|
||
MOVEM T3,3(T1) ;STORE THAT IN FIRST PART
|
||
MOVSM T3,-1(T2) ; AND SWAP OF IT IN LAST CHECK WD
|
||
RETSKP ;RETURN
|
||
;ROUTINE TO RETURN MEMORY
|
||
;CALL T1 := ADDRESS OF THE DMR BUFFER
|
||
;RETURN CPOPJ ;ALWAYS
|
||
GIVDRB::MOVE T2,DMRMSD+MD.ALL(T1) ;RECOVER ORIGINAL BYTE COUNT
|
||
MOVEI T2,<DMRHDL*4>+3+4(T2) ;GET LENGTH (IN BYTES)
|
||
LSH T2,-2 ; AND CONVERT THAT TO WORDS
|
||
MOVE T3,T2 ;COPY LENGTH
|
||
ADD T3,T1 ; AND RELOCATE BY BLOCK ADDRESS
|
||
MOVE T4,[SIXBIT /DMRRMD/] ;GET CHECK WORD
|
||
CAME T4,3(T1) ;CHECK FIRST ONE
|
||
PUSHJ P,NTDSTP## ;CHECK WORD CLOBBERED
|
||
MOVS T4,T4 ;GET VALUE FOR LAST CHECK WORD
|
||
CAME T4,-1(T3) ; AND COMPARE THAT
|
||
PUSHJ P,NTDSTP## ;CHECK WORD CLOBBERED
|
||
EXCH T1,T2 ;IDIOTIC AC CONVENTIONS
|
||
PUSHJ P,GIVWDS## ;RETURN STORAGE
|
||
POPJ P, ; AND WE'RE DONE
|
||
;THESE ARE THE BYTE POINTERS INTO THE VARIOUS FIELDS OF THE
|
||
;DEVICE NAME (USED BY TSTDMR)
|
||
|
||
DRNDMR: POINT 6,P1,18+6-1 ;KONTROLLER NUMBER
|
||
|
||
|
||
|
||
|
||
|
||
|
||
DRSEND::PRGEND
|
||
TITLE DMRINT - SERVICE FOR DMR11 V002
|
||
SUBTTL T. LITT 21 NOV 81
|
||
|
||
;From D8KINT V026
|
||
|
||
SEARCH F, S ,NETPRM, D36PAR
|
||
$RELOC
|
||
$HIGH
|
||
|
||
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
|
||
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
|
||
;
|
||
;COPYRIGHT (C) 1981,1983 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
|
||
XP VDMRINT,002
|
||
|
||
DMRINT::ENTRY DMRINT
|
||
|
||
PURGE NETOFF,NETON
|
||
|
||
COMMENT @
|
||
|
||
EHPL MI RTPADE NIISED A DP1P1
|
||
|
||
END OF COMMENT @
|
||
|
||
Comment @ DMRINT Functional Description
|
||
|
||
DMRINT is the actual physical DMR driver. It interfaces to the rest of the
|
||
monitor thru the standard DECnet ROUTER/DLL interface. Other users, such as
|
||
ANF, UUOCON, or even IBMCOMM translate their private protocols to ours thru
|
||
the interface modules (D8RINT, DMRSER, D6RINT).
|
||
|
||
Requests from users are vectored thru DMRDSP, and use function codes of
|
||
the form 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==<<COMSIZ*4-EBFIN0>/<4*DR%NBF*2>>*4 ;Bytes/buffer
|
||
;Bytes/reg - base-tbl-siz / #buf/chn*#chn rounded to 10 wd /4*4
|
||
TBFSIZ==EBFSIZ/4 ;10 Words/buffer
|
||
IFN <TBFSIZ-DMRMMS>,<Printx ?DMRMMS wrong, update NETPRM>
|
||
EBFOU0==EBFIN0+<DR%NBF*EBFSIZ> ;11 Address of Output Buffer 0
|
||
TBFOU0==TBFIN0+<DR%NBF*TBFSIZ> ;10 Address of Output Buffer 0
|
||
;The following symbols are for information/debugging only
|
||
;The code should work for N page comm regions, and M buffers
|
||
EBFIN1==EBFIN0+EBFSIZ ;11 Address of Input Buffer 1
|
||
TBFIN1==TBFIN0+TBFSIZ ;10 Address of Input Buffer 1
|
||
EBFOU1==EBFOU0+EBFSIZ ;11 Address of Output Buffer 1
|
||
TBFOU1==TBFOU0+TBFSIZ ;10 Address of Output Buffer 1
|
||
|
||
;Compute wasted space (Due to rounding (word boundary))
|
||
WASTE==COMSIZ-<TBFOU0+<DR%NBF*TBFSIZ>> ;10 Words wasted at end of Comm region
|
||
IFL WASTE,<Printx ? DMR11 Communications Region too BIG!>
|
||
|
||
;Compute total wasted space
|
||
WASTE==COMSIZ*4-<^D128 + <2*DR%NBF*EBFSIZ>> ;Total wasted bytes
|
||
SUBTTL DEFINITIONS -- DMR11
|
||
|
||
;DMR-11 BIT DEFINITIONS
|
||
|
||
DMRSL0==0 ;SEL0
|
||
DMRBS0==0 ;BSEL0
|
||
DMRTBI==0 ;TRANSMIT BUFFER IN COMMAND
|
||
DMRCTI==1 ;CONTROL IN COMMAND
|
||
DMRHLT==2 ;HALT PROTOCOL COMMAND
|
||
DMRBSI==3 ;BASE IN COMMAND
|
||
DMRRBI==4 ;RECEIVE BUFFER IN COMMAND
|
||
DMRRQI==40 ;REQUEST INPUT PERMISSION
|
||
DMRIEI==100 ;INTERRUPT A ENABLE ON RDI
|
||
DMRRDI==200 ;DMR IS READY TO INPUT COMMAND
|
||
DMRSSU==400 ;STEP DMR 1 MICROINSTRUCTION
|
||
DMRRMI==1000 ;ROM IN - DMR WILL XCT NEXT INSTR FROM SEL6
|
||
DMRRMO==2000 ;ROM OUT - DMR WILL PRESENT NEXT CROM LOC OR
|
||
; MICROINSTR IN SEL6
|
||
DMRLUL==4000 ;TTL LOOP LINE UNIT SERIAL DATA
|
||
DMRSLU==10000 ;STEP LINE UNIT 0 RCV SHIFT 1 XMT SHIFT
|
||
DMRIMD==20000 ;INHIBIT MICRODIAGNOSTICS (COMPLEMENT SWITCH)
|
||
DMRMRC==40000 ;MASTER CLEAR DMR (SELF-CLEARING)
|
||
DMRRUN==100000 ;RUN
|
||
DMRBS1==1 ;BSEL1
|
||
DMRSL2==2 ;SEL2
|
||
DMRBS2==2 ;BSEL2
|
||
DMRTBO==0 ;TRANSMIT BUFFER OUT
|
||
DMRRBO==4 ;RECEIVE BUFFER OUT
|
||
DMRCTO==1 ;CONTROL OUT
|
||
DMRIEO==100 ;INTERRUPT ENABLE ON RDO
|
||
DMRRDO==200 ;DMR HAS OUTPUT A NEW COMMAND
|
||
DMRBS3==3 ;BSEL3
|
||
DMRMTF==1 ;MICROPROCESSOR BOARD TEST FAILED
|
||
DMRLTF==2 ;LINE UNIT BOARD TEST FAILED
|
||
DMRMDI==100 ;MICRODIAGNOSTICS WERE INHIBITED
|
||
DMRMDR==200 ;MICRODIAGNOSTICS RAN
|
||
DMRSL4==4 ;SEL4
|
||
DMRBS4==4 ;BSEL4
|
||
;MODEM STATUS BITS -- VALID WHEN RDI HAS SET
|
||
DMRMCD==1 ;MODEM CARRIER DETECT/RCVR ACTIVE
|
||
DMRMSB==2 ;RS449 STANBY LEAD
|
||
DMRMCS==4 ;CLEAR TO SEND
|
||
DMRMSR==10 ;DATA SET READY
|
||
DMRMHD==20 ;LINE UNIT IN HDX MODE
|
||
DMRMRS==40 ;REQUEST TO SEND
|
||
DMRMTR==100 ;DATA TERMINAL READY
|
||
DMRMRG==200 ;RING
|
||
DMRMTM==2000 ;MODEM IN TEST MODE
|
||
DMRMSQ==40000 ;SIGNAL QUALITY (CARRIER DETECT LOOKALIKE)
|
||
DMRMSR==100000 ;SIGNAL RATE
|
||
DMRBS5==5 ;BSEL5
|
||
DMRSL6==6 ;SEL6
|
||
DMRBS6==6 ;BSEL6
|
||
;MORE MODEM STATUS BITS
|
||
DMRMRH==1 ;RTS HOLD - IN FDX, LT 1MBAUD DMR HOLDS RTS WHEN
|
||
; LINE IDLE, EXCEPT ERROR RECOVERY
|
||
DMRMHX==20 ;LINE UNIT IN HDX MODE
|
||
DMRMTX==100 ;DATA TERMINAL READY
|
||
;CONTROL-OUT STATUS BITS
|
||
DMRONK==1 ;NAK THRESHOLD EXCEEDED
|
||
DMROTO==2 ;REP TIMEOUT (7 CONSEC REPS XMITTED)
|
||
DMRONB==4 ;7 NAKS RECEIVED DUE TO NO BUFFER
|
||
DMROMM==10 ;MAINT MESSAGE RECEIVED (FATAL)
|
||
DMROLM==20 ;MESSAGE LONGER THAN BUFFER RECEIVED (FATAL)
|
||
DMROHU==100 ;DSR DROPPED (OTHER END HUNG UP)
|
||
DMROSR==200 ;START RECEIVED WHILE RUNNING (FATAL)
|
||
DMRONX==400 ;NXM WHILE XMT/RCV/BASE TABLE UPDATE (FATAL)
|
||
DMROHC==1000 ;HALT COMPLETE
|
||
;CONTROL-IN COMMAND BITS
|
||
DMRILS==4000 ;Use long start timer
|
||
DMRIHD==2000 ;Use half duplex mode
|
||
DMRIMT==400 ;Use DDCMP maintenance mode
|
||
DMRBS7==7 ;BSEL7
|
||
;MODEM STATUS BITS - VALID ONLY AFTER BASE TABLE ASSIGNED
|
||
DMRMPS==1 ;PROGRAM SELECTED - MAINT BIT
|
||
DMRMIM==10 ;INTEGRAL MODEM SELECTED
|
||
DMRM35==20 ;V.35 MODEM SELECTED
|
||
DMRM32==100 ;RS232-C OR RS423-A MODEM SELECTED
|
||
DMRM22==200 ;RS422-A MODEM SELECTED
|
||
SUBTTL DMRONC -- ONCE ONLY ROUTINE FOR DMR
|
||
|
||
;THIS ROUTINE IS CALLED BY SYSINI. IT VERIFYS THAT ALL DMR11
|
||
;UNITS SPECIFIED BY MONGEN ACTUALLY EXIST.
|
||
|
||
|
||
DMRONC::PUSHJ P,SAVE4## ;HERE FROM ONCE. WE USE 4 P'S
|
||
|
||
SETZ P1, ;P1 IS CURRENT DMR INDEX, START WITH DMR ZERO
|
||
|
||
DMROCP: CAIL P1,M.DMRN## ;LOOP OVER ALL DMRs
|
||
POPJ P, ;IF WE'VE DONE THEM ALL, RETURN
|
||
|
||
SKIPN W,DMRTBL##(P1) ;GET THE ADDRESS OF THE NEXT DMR BLOCK
|
||
AOJA P1,DMROCP ; IF IT'S NOT THERE, TRY THE NEXT ONE
|
||
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,<LD.DMR_^D9(1B0)>(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>>,<PRINTX ? CALUSR vector wrong for TYP>
|
||
IFE FT,<
|
||
PUSHJ P,NTDSTP## ;;++ User not supported in this monitor!
|
||
HALT .-1> ;;Fill out the block
|
||
IFN FT,<
|
||
MOVE T2,DMR'BLK(W) ;;Point to user's block
|
||
PJRST DSP> ;;And dispatch to user's entry vector
|
||
>;Define X
|
||
|
||
CALDVN: X(NOBODY,1,USR,NOBDSP##) ;The null user
|
||
X(ANF10,FTNET,FEK,D8RK2U##) ;Real networks
|
||
X(DECNET,FTDECN,LBK,CALPPI) ;Imitation networks
|
||
X(PROGRAM,1,DDP,DMRKTU##) ;UUOCON
|
||
X(IBMCOMM,1,DLX,D6RK2U##) ;Non-networks
|
||
IFN <.-CALDVN-<2*<DD.MAX+1>>>,<PRINTX ? CALDVN user entry missing!>
|
||
|
||
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
|