Trailing-Edge
-
PDP-10 Archives
-
tops10_703_distr_bb-x140b-sb
-
10,7/703mon/d8kint.mac
There are 3 other files named d8kint.mac in the archive. Click here to see a list.
TITLE D8KINT - SERVICE FOR KMC-11/DUP-11 V100
SUBTTL W. E. MATSON/TARL/TL 29 OCT 85
SEARCH F, S, NETPRM, D36PAR
$RELOC
$HIGH
.DIRECT FLBLST
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1978,1986>
;COPYRIGHT (C) 1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.
;
;
;
XP VD8KINT,100
Comment @ Loose ends.
o Worry about 2 lines to same node. Also worry about
"connected to self". (ie. lines connected together.)
End Comment @
D8KINT::ENTRY D8KINT
SUBTTL FEK -- FEK INTERFACE FOR D8KINT
;D8KINT FEK INTERFACE. ENTRIES ARE:
;
; D8KONC ;INITIALIZATION FOR FEK (ZERO KDL PAGE).
; D8KSEC ;ONCE A SECOND CODE.
; D8KRDD ;SET UP A READ REQUEST (NO-OP).
; D8KWRT ;QUEUE A BUFFER FOR OUTPUT.
; D8KCRS ;SHUT DOWN A LINE
; D8KDWN ;NOT USED
; D8KUP ;NOT USED
; D8KSTC ;CALLED WITH "U" := STC MESSAGE TO SEND
;
;FEKINT IN NETSER IS CALLED WHENEVER
;
; A KDL GOES OFFLINE (FF.DWN INTERRUPT)
; A MESSAGE IS RECEIVED (FF.IN INTERRUPT)
; AN XMIT MESSAGE IS ACKED (FF.OUT INTERRUPT)
D8KDSP::CAIL T1,FF.ONC ;RANGE CHECK 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## ;USE NETSER DEFAULT (FOR NODE ID. ETC)
JRST D8KRDD ;SET UP A READ REQUEST
JRST D8KWRT ;SET UP A WRITE REQUEST
JRST D8KCRS ;CRASH THE FEK (CPU WENT (WILL BE) DOWN?)
JRST CPOPJ## ;FEK DOWN (NOT USED)
JRST CPOPJ## ;FEK UP (NOT USED)
JRST D8KSTC ;CALLED WITH "U := STC MESSAGE TO SEND"
JRST D8KCRS ;SYSTEM SLEEPING AND
JRST D8KCRS ; WAKING IS TOO COMPLEX TO THINK ABOUT NOW
D8KCRS: PUSHJ P,NTDSTP## ;THIS SHOULD NEVER EVER HAPPEN. WE
POPJ P, ; DO BETTER TIMING THAN NETSER. IF
; IT DOES HAPPEN, SOMETHING IS HORRIBLY
; WRONG.
D8KRDD: AOS FEKBSI(J) ;SET FLAG SAYING WE HAVE AN INPUT BUFFER
POPJ P, ;KDPINT CALLS US WHEN DATA ARRIVES
D8KWRT: NETOFF ;FIRST DISABLE INTERRUPTS
SKIPG FEKOCT(J) ;IF THERE ARE NO MORE MESSAGES,
JRST NTONPJ## ;THEN WE ARE 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 KDL PAGE ADDRESS
PUSH P,T3 ;SAVE THE MSG POINTER
PUSHJ P,KDADSP## ;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,D8KK2D ;DO WHATEVER NEEDED
JRST D8KWRT] ;CHECK FOR MORE
POP P,T3 ;RESTORE MSG POINTER
JRST D8KWRT ;AND GO CHECK FOR MORE
D8KSTC: 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 D8KST1 ; 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,KDADSP## ; 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
D8KST1: 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,KDADSP## ;CALL KDPINT 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,D8KK2D ;DO WHATEVER NEEDED
JRST CPOPJ1] ;GIVE GOOD RETURN
RETSKP ; AND GIVE A GOOD RETURN
SUBTTL KDPINT - INTERFACE FROM KMC/DUP DRIVER
;D8KK2D HERE ON DISPATCH ENTRYS FROM THE KMC-11/DUP-11 KONTROLLER
;CALL T1 := FUNCTION CODE (BELOW)
; T2 := FEK/LINE-BLOCK ADDRESS
; T3 := BUFFER ADDRESS OR BYTE COUNT
D8KK2D::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,@D8KKKD(T1) ;DISPATCH BASED ON FUNCTION
POP P,J ;RESTORE ANOTHER AC
POP P,U ;RESTORE AN AC
POPJ P, ;BACK TO WHOMEVER
D8KKKD: 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: MOVEI T1,DC.FIL ;NOW TELL THE KONTROLLER
MOVE T2,FEKUNI(J) ; TO INITIALIZE ITSELF IN "NORMAL" MODE
PUSHJ P,KDADSP## ;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
;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
MOVE 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 UTILITY ROUTINES.
;CALNET ROUTINE TO CALL NETSER. PRESERVES REGISTERS THAT NETSER
; LIKES TO CLOBBER
;CALL PUSHJ P,CALNET
;RETURN POPJ P,
CALNET: PUSH P,W ;W (NDB)
PUSH P,F ; F (DDB)
PUSH P,T4 ; AND T4 (???)
PUSHJ P,FEKINT## ;CALL NETSER
POP P,T4 ;AND THEN
POP P,F ; RESTORE
POP P,W ; EVERYTHING
POPJ P, ;AND RETURN
PRGEND ;OF D8KINT.MAC
TITLE KDPSER - USER INTERFACE FOR KDP CONTROL V001
SUBTTL W. E. MATSON 12 FEB 80
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 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VKDPSER,001
Comment @ Loose ends.
End Comment @
KDPSER::ENTRY KDPSER
KDPMXQ==^D10 ;MAXIMUM NUMBER OF INPUT OR OUTPUT MSGS
; ALLOWED FOR A USER OF THE KDP: 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 KDP 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 KDPDDB - PROTOTYPE DDB FOR THE KDP DEVICE
DEFINE X(NAME<%%%OFF>,SIZE<1>),<NAME==:<%%%OFF==%%%OFF+SIZE>-SIZE>
%%%OFF==DEVLLD ;INITIALIZE THE OFFSET
X DEVKDL ;XWD KDL-ADDRESS, INPUT MSG QUEUE
X DEVOBC ;COUNT OF OUTPUT BUFFERS OUTSTANDING
X KDPDBL,0 ;LENGTH OF A KDP 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 KDPDDB+OFFSET ;GO TO THE RIGHT WORD
EXPR ;ASSEMBLE IN THE EXPRESSION
>
$LOW ;WE NEED TO LINK THIS IN
KDPDDB::X DEVCHR,<XWD <6*HUNGST>+DVC2IO,KDLMMS+1> ;BUFFER SIZE
X DEVSER,<XWD 0,KDPUDS> ;DEFINE NETWORK DISPATCH VECTOR
X DEVMOD,<XWD DVIN!DVOUT,1_BYTMOD>
X DEVSTA,<XWD DEPLEN!.TYKDP,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 KDPUUO -- UUO ENTRY TO KDPSRV
;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 TSTKDP)
JRST CPOPJ## ;(-1) HUNG DEVICE
KDPUDS: JRST KDPREL ;(0) RELEASE (MAKE SURE KDL DOESN'T POINT)
JRST KDPCLO ;(1) CLOSE OUTPUT
JRST KDPOUT ;(2) OUTPUT
JRST KDPIN ;(3) INPUT
;TSTKDP ROUTINE CALLED FROM UUOCON DURING A PHYSICAL DEVICE SEARCH
;CALL T1 := DEVICE NAME
;RETURN CPOPJ NOT KDP, NOT IN OPEN UUO, ALREADY ASSIGNED OR NO PRIVS
; CPOPJ1 F := DDB ADDRESS
TSTKDP::HLRZ T2,T1 ;GET THE RH OF THE NAME,
CAIE T2,'KDP' ; AND IF IT ISN'T "KDP"
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,KDNMPA ;GET THE MULTI-POINT ADDRESS
JUMPN T1,CPOPJ## ;THERE BETTER NOT BE ANY. WE DON'T SUPPORT IT
LDB T1,KDNKDP ;GET THE CONTROLLER NUMBER
CAIG T1,KDPMAX## ; AND MAKE SURE IT'S BOTH LEAGAL
SKIPN W,KDPTBL##(T1) ; AND THAT IT EXISTS
POPJ P, ;IF IT DOESN'T EXIST, USER CAN'T OPEN IT
LDB T1,KDNKDL ;GET THE LINE NUMBER ON THE KONTROLLER
CAML T1,KDPDPN(W) ; MAKE SURE THAT THE DUP NUMBER IS
POPJ P, ; IN RANGE. IF NOT, FAIL
MOVEI F,KDPKDL(W) ;NOW CALCULATE THE ADDRESS OF THE
ADD F,T1 ; POINTER TO THE KDL PAGE
SKIPN F,(F) ;GET THE POINTER,
POPJ P, ;IF NO POINTER, THEN DUP DOESN'T EXIST
SKIPE KDLDDB(F) ;SEE IF SOMEONE ELSE OWN'S THE DDB
JRST CLRFPJ ; IF SO, ZERO F AND RETURN
;NOW BUILD A DDB
MOVEI T2,KDPDBL ;GET THE LENGTH OF A KDP DDB
PUSHJ P,GETWDS## ; AND ALLOCATE THE SPACE
JRST CLRFPJ ; IF NOT ENOUGH, GIVE THE FAIL RETURN
MOVSI T2,KDPDDB ;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,KDPDBL-1(T1) ;INITIALIZE THE NEW ONE
HRLM T1,DEVSER+KDPDDB ; AND SET UP THE LINKS
DDBSRU ;DDB IS IN PLACE, UNLOCK THE DDB CHAIN
HRLM F,DEVKDL(T1) ;MAKE THE DDB POINT TO THE KDL BLOCK
EXCH T1,KDLDDB(F) ;MAKE THE KDL BLOCK POINT TO THE DDB
SKIPE T1 ;JUST A PARANOID CHECK.
PUSHJ P,NTDSTP## ; I DON'T THINK THIS IS POSSIBLE
MOVE F,KDLDDB(F) ;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
;KDPREL ROUTINE TO PROCESS THE "RELEASE" FUNCTION OF A KDP
;CALL F := ADDRESS OF THE DDB
;RETURN CPOPJ ;ALWAYS
KDPREL: PUSHJ P,KDPCKO ;SEE IF WE ARE THE LINE'S "OWNER"
JRST KDPRL1 ;IF NOT, DON'T MESS WITH THE KONTROLLER
MOVEI T1,DC.FHL ;GET THE "HALT" FUNCTION CODE
HLRZ T2,DEVKDL(F) ;GET THE ADDRESS OF THE KDL PAGE
PUSHJ P,KDODSP## ;CALL THE KONTROLLER. TELL HIM TO HALE
JFCL ;IF WE DON'T OWN THE LINE WE DON'T CARE
KDPRL1: SKIPE DEVOBC(F) ;ALL BUFFERS SHOULD BE RETURNED BY NOW
PUSHJ P,NTDSTP## ;SINCE THEY ARE RETURNED BY THE "HALT" CALL
HLRZ T1,DEVKDL(F) ;CLEAR THE KDL'S POINTER TO THE DDB
SETZM KDLDDB(T1) ; SO WE DON'T GET ANY MORE INTERRUPTS
;NOW FREE ANY MSGS ON THE INPUT QUEUE
KDPRL2: KDPOFF ;PROBABLY DON'T NEED TO, BUT...
HRRZ T1,DEVKDL(F) ;GET THE NEXT INPUT MESSAGE
JUMPE T1,KDPRL3 ;IF NONE, THEN WE ARE DONE
HRRZ T2,MB.NXT(T1) ;GET THE MESSAGE AFTER THAT ONE
HRRM T2,DEVKDL(F) ; AND MAKE SURE WE GET IT NEXT
KDPON ;QUEUE IS CONSISTANT AGAIN
PUSHJ P,GIVKDB ;RETURN THE BUFFER
JRST KDPRL2 ; AND GO GET THE NEXT ONE
KDPRL3: KDPON ;ALL MESSAGES HAVE BEEN FREED
POPJ P, ; AND WE'RE DONE
;ZAPKDP ROUTINE TO DESTROY A KDP DDB
;CALL F := DDB ADDRESS
;RETURN CPOPJ
ZAPKDP::PUSHJ P,KDPREL ;FIRST "RELEASE" IT (IN CASE OF SWAP READ ERR)
MOVEI T2,KDPDDB ;GET THE STARTING DDB
ZAPKD1: 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 ZAPKD1 ;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,KDPDBL ; AND IT'S LENGTH
PUSHJ P,GIVWDS## ;FREE THE STORAGE
POPJ P, ;AND WE'RE DONE.
;KDPIN ROUTINE TO PROCESS THE IN UUO FOR THE KDP 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 ?KDPSER assumes that ACs M and T6 are the same
PRINTX ?Because it knows that M is OK to trash>
KDPIN: 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,KDPCKM ;MAKE SURE WE'RE IN THE CORRECT MODE
KILOOP: PUSHJ P,KDPONL ;MAKE SURE THE KMC 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,DEVKDL(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
KDPOFF ;KEEP INTERRUPT LEVEL OUT
HRRZ T1,MB.NXT(P2) ;GET THE ADDRESS OF THE NEXT MESSAGE
HRRM T1,DEVKDL(F) ; AND MAKE THAT ONE BE THE FIRST
KDPON ;RE-ENABLE INTERRUPTS
MOVE T1,P2 ;GET THE KDB ADDR BACK
PUSHJ P,GIVKDB ;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
;KDPOUT ROUTINE TO PROCESS THE OUTPUT UUO FOR THE KDP DEVICE
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;ALWAYS
KDPCLO:
KDPOUT: 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,KDPCKM ;CHECK THE MODE
KOLOOP: PUSHJ P,KDPONL ;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,KDPMXQ ;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 KDP DATA BLOCK TO HOLD THE DATA
KOLOO1: EXCTUX <HRRZ T1,1(P1)> ;GET THE NUMBER OF USER BYTES
CAILE T1,KDLMMS*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,GETKDB ;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,DEVKDL(F) ; TO THIS KDL
MOVE T3,P2 ; AND THIS IS THE MESSAGE BLOCK
PUSHJ P,KDODSP## ;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,KDPIDS ;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.KDP ;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.KDP ;OTHERWISE, GO INTO
PUSHJ P,ESLEEP## ; EVENT WAIT, AND
JRST KOLOOP ; THEN SEE IF WE CAN DO OUTPUT
;KDPIAV ROUTINE TO SIGNAL THAT KDP INPUT IS AVAILABLE
KDPIAV: 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.
;KDPOAV ROUTINE TO SIGNAL THAT OUTPUT HAS BEEN COMPLETED
KDPOAV: 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
;KDPCKO ROUTINE TO CHECK OWNERSHIP OF THE KDL BLOCK.
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;KDL IS NOT IN "PROGRAM" MODE
; CPOPJ1 ;KDL IS IN "PROGRAM" MODE
KDPCKO: HLRZ T1,DEVKDL(F) ;GET THE KDL PAGE ADDRESS
HRRZ T1,KDLUSR(T1) ;GET THE USER CODE
CAIN T1,DD.PRO ;IF IT IS "PROGRAM"
AOS (P) ; THEN GIVE A GOOD
POPJ P, ; RETURN
;KDPONL ROUTINE TO CHECK TO SEE IF THE KDP IN "ON LINE"
;CALL F := DDB ADDRESS
;RETURN CPOPJ ;NOT OWNED OR NOT ONLINE (ERROR BITS SET)
; CPOPJ1 ;KDP APPEARS TO BE READY FOR I/O
KDPONL: PUSHJ P,KDPCKO ;FIRST SEE IF WE OWN THE LINE
JRST K.IMPM ; IF NOT, THEN IT'S IMPROPER MODE
HLRZ T1,DEVKDL(F) ;GET THE KDL PAGE ADDRESS
HRRZ T1,KDLKDP(T1) ; AND FROM THAT GET THE KDP ADDRESS
JUMPE T1,K.DERR ;IF NO KDP YET (NOT INITED) THEN NOT UP
MOVE T1,KDPSTS(T1) ; AND FROM THAT GET THE KDP STATUS
TLNN T1,(KDPSRU) ;SEE IF WE'RE RUNNING
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
PUSH P,F ;PRESERVE THE DDB ADDRESS
HLRZ F,DEVKDL(F) ;GET THE KDL PAGE ADDRESS
LDB T1,KDLSTA## ; AND GET THE STATE
POP P,F ;RESTORE THE DDB ADDRESS
TRNE S,IOSMAI ;IF WE ARE TRYING MAINT MODE,
JRST KDPON1 ; GO CHECK ONLINE DIFFERENTLY
CAIGE T1,KD%STR ;IF SUPPOSED TO BE NORMAL, BUT NOT
JRST K.DERR ; THEN WE MUST HAVE SCREWED UP
CAIE T1,KD%RUN ;IF WE'RE NOT RUNNING, THEN
JRST K.DTER ; IT'S A "SOFT" AREA. (WAIT FOR ONLINE)
RETSKP ;IF IN RUN, THEN ALL'S OK
KDPON1: CAIE T1,KD%MAI ;IF WE'RE NOT IN MAINT MODE, THEN
JRST K.DERR ; IT'S AN ERROR
RETSKP ;OTHERWISE WE'RE "ONLINE"
;KDPCKM ROUTINE TO CHECK/SET THE MAINT/NORMAL MODE OF THE LINE
;CALL F := DDB
;RETURN CPOPJ ;ALWAYS (STARTS THE LINE IN MAINT/NORMAL)
KDPCKM: PUSHJ P,KDPCKO ;SEE IF WE ARE THE LINE'S OWNER
POPJ P, ; IF NOT, DON'T DO ANYTHING
PUSH P,F ;FIRST PRESERVE THE DDB POINTER
HLRZ F,DEVKDL(F) ;FROM THAT GET THE KDL PAGE ADDRESS
LDB T1,KDLSTA## ;FROM THAT, GET THE STATE
POP P,F ;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 KDPCK1 ; THEN DGO CHECK DIFFERENTLY
CAIL T1,KD%STR ;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 KDPCK2 ; SO GO TO COMMON CODE TO DO SO
KDPCK1: CAIN T1,KD%MAI ;IF WE'RE IN MAINT MODE
POPJ P, ; THEN IT'S OK
MOVEI T1,DC.FSM ;WE WANT TO PUT IN MAINT STATE
KDPCK2: PUSH P,T1 ;SAVE THE "DESTINATION STATE"
MOVEI T1,DC.FHL ;FIRST WE MUST "HALT" THE LINE
HLRZ T2,DEVKDL(F) ; GET THE LINE ID
PUSHJ P,KDODSP## ; 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,DEVKDL(F) ; AND THE LINE ID
PUSHJ P,KDODSP## ;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
;KDPIDS INTERRUPT LEVEL DISPATCH FROM KMC-11/DUP-11 KONTROLLER
KDPIDS::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 KDL PAGE ADDRESS (OR WHAT EVER)
MOVE F,T2 ;SET F TO POINT TO THE DDB
PUSHJ P,@KDPID1(T1) ;CALL THE APPROPRIATE INTERRUPT ROUTINE
POP P,F ;RESTORE "F"
POPJ P, ; AND RETURN TO THE KONTROLLER
KDPID1: IFIW KD.PUP ;(00) PRIMARY PROTOCOL UP
IFIW KD.PDN ;(01) PRIMARY PROTOCOL DOWN
IFIW KD.MAI ;(02) MAINT MSG RECEIVED IN NORMAL PROTOCOL
IFIW KD.STR ;(03) START MSG RECEIVED IN MAINT PROTOCOL
IFIW KD.ODN ;(04) OUTPUT DONE
IFIW KD.OND ;(05) OUTPUT NOT DONE
IFIW KD.IDN ;(06) INPUT DONE
IFIW KD.RQI ;(07) REQUEST INPUT BUFFER
IFIW NTDSTP## ;(10) LINE CREATION
IFIW NTDSTP## ;(11) LINE DISSOLUTION
;PROTOCOL UP
KD.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 KD.WAK ; AND WAKE UP ANY SERVICE ROUTINES
;PROTOCOL DOWN
KD.PDN: PUSHJ P,K.DERR ;SIGNAL AN IO DEVICE ERROR
PUSHJ P,PSIDWN## ; AND GIVE AN OFFLINE INTERRUPT
KD.WAK: PUSHJ P,KDPIAV ;NOW MAKE SURE THAT THE SERVICE ROUTINE
PUSHJ P,KDPOAV ; 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
KD.MAI: ;IF EITHER ONE OF THE "WRONG TYPE" OF
KD.STR: PUSHJ P,K.IMPM ; MESSAGES ARRIVES, SAY "IMPROPER MODE"
PJRST KD.PDN ; AND TREAT IT AS "PROTOCOL DOWN"
;KD.IDN ROUTINE TO PROCESS INPUT MESSAGES FROM THE KDP
KD.IDN: KDPOFF ;NO INTERRUPTS WHILE HACKING QUEUE
HRRZ T2,DEVKDL(F) ;GET THE ADDRESS OF INPUT MESSAGE QUEUE
JUMPE T2,[HRRM T3,DEVKDL(F) ; IF NO QUEUE, START ONE AND
JRST KD.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
KD.ID1: KDPON ;RE-ENABLE INTERRUPTS
PUSHJ P,KDPIAV ;SIGNAL THAT INPUT IS AVAILABLE
POPJ P, ; AND RETURN
;KD.ODN KD.OND ROUTINES TO PROCESS RETURNED OUTPUT MESSAGES
KD.ODN:
KD.OND: MOVE T1,T3 ;GET THE ADDRESS OF THE SPENT MSG
PUSHJ P,GIVKDB ; AND FREE IT
SOS DEVOBC(F) ; DECREMENT THE COUNT OF OUTSTANDING MSGS
PJRST KDPOAV ; AND WAKE UP THE DRIVER
;KD.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
KD.RQI: MOVE T2,DEVIOS(F) ;GET DEVICE STATUS
TRNE T2,IOIMPM ;IN WRONG PROTOCOL?
JRST KD.RQ1 ;YES, DON'T ACCEPT MESSAGE
KDPOFF ;LOCK THE INPUT CHAIN WHILE WE COUNT BUFFERS
HRRZ T2,DEVKDL(F) ;GET THE ADDRESS OF THE FIRST MESSAGE
MOVEI T1,KDPMXQ ; 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
KDPON ; IF THE QUOTA IS EXHAUSTED
POPJ P,] ; THEN RETURN (T1 := 0)
KDPON ;THE CHAIN IS CONSISTANT AGAIN
MOVE T1,T3 ;GET THE BYTE COUNT
PUSHJ P,GETKDB ; AND ALLOCATE A MESSAGE BUFFER
KD.RQ1: SETZ T1, ;IF NONE, TELL THE KONTROLLER
POPJ P, ;RETURN
;ROUTINES TO GET AND FREE KDP 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 /KMCDUP/
; 4 \
; 5 \ MESSAGE SEGMENT
; 6 / DESCRIPTOR GOES HERE
; 7 /
; 8 \
; . \ DATA
; . / DATA
; N-1 /
; N CONTAINS SIXBIT /DUPKMC/ (CHECK WORD)
KDPHDL==4+MD.LEN ;LENGTH OF KDP HEADER IN WORDS
KDPMSD==4 ;OFFSET TO MSD FROM BEG OF BLK
GETKDB::PUSH P,T1 ;SAVE BYTE COUNT
MOVEI T2,<KDPHDL*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 KDPMSD+MD.NXT(T1) ;MAKE SURE THERE IS ONLY 1 MSD
MOVEM T2,KDPMSD+MD.BYT(T1) ; AND STORE THAT FOR USER
MOVEM T2,KDPMSD+MD.ALA(T1) ; & PUT IT IN "ALLOC INFO" AREA
MOVEM T2,KDPMSD+MD.ALL(T1) ; ...
MOVSI T3,(POINT 8) ;MAKE A BYTE POINTER TO DATA
HRRI T3,KDPMSD+MD.LEN(T1) ; AREA AFTER HEADER
MOVEM T3,KDPMSD+MD.PTR(T1) ; AND STORE THAT
MOVEM T3,KDPMSD+MD.AUX(T1) ;STORE OUTPUT BYTE POINTER ALSO
MOVEI T3,KDPMSD(T1) ;GET ADDRESS OF MSD
MOVEM T3,MB.FMS(T1) ; AND STORE IT IN MESSAGE BLOCK
MOVEI T2,<KDPHDL*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 /KMCDUP/] ;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 KDP BUFFER
;RETURN CPOPJ ;ALWAYS
GIVKDB::MOVE T2,KDPMSD+MD.ALL(T1) ;RECOVER ORIGINAL BYTE COUNT
MOVEI T2,<KDPHDL*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 /KMCDUP/] ;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 TSTKDP)
KDNMPA: POINT 10,P1,35 ;LOW 10 BITS ARE MULTI POINT ADDR
KDNKDL: POINT 4,P1,25 ;NEXT 4 BITS ARE LINE NUMBER
KDNKDP: POINT 4,P1,21 ;NEXT 4 BITS ARE KONTROLLER NUMBER
XLIST
$LIT ;DON'T LIST LITERALS
LIST
KDSEND::PRGEND
TITLE KDPINT - SERVICE FOR KMC-11/DUP-11 V002
SUBTTL W. E. MATSON/TL 21-Oct-84
SEARCH F, S, NETPRM, D36PAR
$RELOC
$HIGH
.DIRECT FLBLST
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1978,1979,1980 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VKDPINT,001
Comment @ Loose ends.
End Comment @
KDPINT::ENTRY KDPINT
COMMENT @
EHPL MI RTPADE NIISED A DP1P1
END OF COMMENT @
OPDEF BLTBU [716B8] ;FAST BYTE TO UNIBUS
OPDEF BLTUB [717B8] ;FAST UNIBUS TO BYTE
SUBTTL KDPPRM -- PARAMETERS FOR THE KMC/DUP
REPMAX==^D20 ;NUMBER OF UNANSWERED REPS IMPLIES DEAD LINE
STRSEC==^D5 ;NUMBER OF SECONDS BETWEEN STARTS
STKSEC==^D2 ;NUMBER OF SECONDS BETWEEN STACKS
REPSEC==^D3 ;NUMBER OF SECONDS BETWEEN REPS
MAXOUT==^D10 ;MAXIMUM NUMBER OF OUTSTANDING UN-ACKED MSGS
T6==T4+2 ;T6 FOR INDEXED MSD PTRS (DECNET)
PURGE NETOFF, NETON ;GET RID OF THE OLD ONES
;COMPUTE CODE FOR "TIMER STOPPED" BASED ON FIELD SIZE
KD%MXT==-1_-^D<36-<<KD%TIM&^O<007700,,0>>_-24>>
SUBTTL DEFINITIONS -- DDCMP
SYN==226
ENQ==005 ;1ST CHAR IN UNNUMBERED MESSAGES
DLE==220 ;1ST CHAR IN BOOTSTRAP MESSAGES
SOH==201 ;1ST CHAR IN NUMBERED MESSAGES
;NUMBERED MESSAGES
; SOH CC1 (CC2 QSYNC SELECT) R N A0 BCC1 DATA BCC2
; CC1&CC2 ARE TOTAL LENGTH OF DATA(BCC1 THRU BCC2 EXCLUSIVE)
; CC1 IS LOWORDER 8BITS OF LENGTH
; CC2 IS HIGH ORDER 6 BITS OF LENGTH
QSYNC==100 ;QSYNC BIT
SELECT==200 ;SELECT BIT
; R IS # OF LAST GOOD MESSAGE RECEIVED
; N IS THIS MESSAGE NUMBER
; A0 ;A0 IS THE DESTINATION STATION ADR(ALWAYS 1)
; BCC1 IS THE 16 BIT CRC ON SOH THROUGH A0 INCLUSIVE
; DATA IS PASSED TO NCL
; BCC2 IS THE 16BIT CRC OF DATA
;UNNUMBERED MESSAGES
;ACK: ENQ ACK (QSYNC SELECT FILL) MSG# FILL A0 BCC1 BCC2
; MSG# IS LAST GOOD MSG RECEIVED
%ACK==1
;NAK: ENQ NAK (QSYNC SELECT RNAK) MSG# FILL A0 BCC1 BCC2
; RNAK IS NAK REASON
; MSG# IS LAST GOOD MSG RECEIVED
%NAK==2
;REP: ENQ REP (QSYNC SELECT FILL) FILL N A0 BCC1 BCC2
; N IS THE LAST MESSAGE# SENT
%REP==3
;START: ENQ STRT (QSYNC SELECT FILL) FILL N A0 BCC1 BCC2
; N IS NEXT NUMBERED MESSAGE TO BE SENT
%START==6
;STACK: ENQ STACK (QSYNC SELECT FILL) R N A0 BCC1 BCC2
; R IS NEXT EXPECTED MESSAGE # FOR RECPTION
; N IS NEXT MESSAGE # FOR TRANSMISSION
%STACK==7
;NAK REASONS
RSNHCK==^D1 ;HEADER BCC ERROR
RSNDCK==^D2 ;DATA BCC ERROR
RSNREP==^D3 ;REP RESPONSE
RSNBTU==^D8 ;BUFFER TEMPORARILY UNAVAILABLE
RSNOVR==^D9 ;RECEIVER OVERRUN
RSNM2L==^D16 ;MESSAGE TOO LONG (FATAL)
RSNHFA==^D17 ;MESSAGE HEADER FORMAT ERROR.
SUBTTL DEFINITIONS -- DUP11
DUPADR==3760300 ;ADDRESS OF 1ST DUP11
DUPUBN==3 ;UNIBUS ADAPTER NUMBER FOR DUP11
DPRCSR==0 ;RECEIVER CSR
DPDTR==000002 ;DATA TERMINAL READY
DPRDBF==2 ;(RO)RECEIVER DATA BUFFER
DPPCSR==2 ;(WO)PARAMETER CONTROL AND STATUS REGISTER
DPTCSR==4 ;TRANSMIT CONTROL AND STATUS REGISTER
DPCBLP==010000 ;EXTERNAL MAINTENCE MODE (CABLE LOOPBACK)
DPCNLP==004000 ;SYSTEMS TEST MODE (CONTROLLER LOOPBACK)
DPMAIN==014000 ;MAINTAINENCE MODE BITS
DPTDBF==6 ;TRANSMIT DATA BUFFER
SUBTTL DEFINITIONS -- KMC11
SEL0==0
BSEL1==1
KMCRUN==100000 ;RUN FLOP
KMCMCL==040000 ;MASTER CLEAR
KMCCWR==020000 ;CRAM WRITE
KMCSLU==010000 ;STEP LINE UNIT
KMCLUL==004000 ;LINE UNIT LOOP
KMCRMO==002000 ;ROM OUTPUT
KMCRMI==001000 ;ROM INPUT
KMCSUP==000400 ;STEP u-PROCESSOR
KMCRQI==000200 ;REQUEST INPUT
KMCIEO==000020 ;INTERRUPT ENABLE OUTPUT
KMCIEI==000001 ;INTERRUPT ENABLE INPUT
SEL2==2
BSEL3==3 ;CONTAINS LINE NUMBER
KMCOVR==100000 ;KMC BUFFER OVER-RUN
KMCRDO==000200 ;READY FOR OUTPUT
KMCRDI==000020 ;READY FOR INPUT
KMCIOT==000004 ;SET FOR RECEIVE CLEARED FOR TRANSMIT
KMCTYP==000003 ;COMMAND TYPE
BASEIN==000003 ;BASE IN
CNTLIN==000001 ;CONTROL IN
BFADIN==000000 ;BUFFER ADDRESS IN
CNTLOU==000001 ;CONTROL OUT
BFADOU==000000 ;BUFFER ADDRESS OUT
SEL4==4
BSEL5==5
;BUFFER DESCRIPTOR LIST ADDRESS
; (BUFFER ADR IN & OUT & CONTROL OUT)
SEL6==6
BSEL7==7
;140000 ;ADR BITS 17 & 16
; (BUFFER ADR IN & OUT & CONTROL OUT)
BFREOM==010000 ;END OF MESSAGE (BUFFER ADR OUT)
BFRENB==020000 ;BUFFER ENABLE (BUFFER ADR IN)
BFRKIL==010000 ;BUFFER KILL (BUFFER ADR IN)
CSRMSK==017770 ;MASK FOR DUP11 CSR ADR (BASE IN)
CDDCMP==100000 ;FLAG THIS A DDCMP LINE (CONTROL IN)
CHALFD==020000 ;FLAG THIS IS HALF DUPLEX (CONTROL IN)
;010000 ;ENABLE SECONDARY STATION (CONTROL IN)
;001000 ;CRC INHIBIT (CONTROL IN)
CENABL==000400 ;FLAG TO ENABLE LINE (CONTROL IN)
COUERR==000377 ;ERROR CODE (CONTROL OUT)
CRAMSZ==2000 ;SIZE OF KMC11 CRAM
DRAMSZ==2000 ;SIZE OF KMC11 DRAM
;BUFFER DESCRIPTOR LISTS ARE STRINGS OF 3 16 BIT WORDS
; 1ST WORD 16 BITS OF BUFFER ADDRESS
; 2ND WORD 16 BIT BYTE COUNT
; 3RD WORD
BDLLDS==100000 ;LAST DESCRIPTOR
BDLRSY==010000 ;RESYNC TRANSMITTER
BDLXAD==006000 ;BUFFER ADDRESS BITS 17 & 16
BDLEOM==001000 ;END OF MESSAGE
BDLSOM==000400 ;START OF MESSAGE
;MESSAGES TO THE KMC11
; BASEIN: BSEL2/ <LINE #>*400+3
; BSEL6/ <DUP11 ADR>&017770
; CONTROL IN: BSEL2/ <LINE #>*400+1
; BSEL6/ FLAGS
; BF AD IN: BSEL2/ <LINE NU>*400+0+<4 IF INPUT>
; BSEL4/ BUFFER DESCRIPTOR LIST ADR
; BSEL6/ FLAGS
; BF AD OUT: BSEL2/ <LINE NU>*400+0+<4 IF RECEIVE>
; BSEL4/ BUFFER DESCRIPTOR LIST ADR
; BSEL6/ FLAGS
; CONTROL OUT: BSEL2/ <LINE NU>*400+1+<4 IF RECEIVE>
; BSEL4/ BUFFER DESCRIPTOR LIST ADR
; BSEL6/ ERROR CODE
;CONTROL OUT BIT MASK DEFINITIONS. THE ROUTINE "CTOTLY" TAKES A
; CONTROL OUT CODE, TALLYS THE CONTROL OUT IN THE KDL BLOCK, AND
; PRODUCES A BIT VECTOR OF ONE BIT REPRESENTING THE CODE. THIS
; BIT VECTOR REPRESENTATION IS USED TO FACILITATE PARALLEL TESTING
; FOR VARIOUS CONDITIONS. BIT DEFINITIONS ARE:
CTLO06==040000 ;ABORT. SHOULD NEVER HAPPEN
CTLO10==020000 ;HEADER CRC ERROR.
CTLO12==010000 ;DATA CRC ERROR.
CTLO14==004000 ;NO RECEIVE BUFFER ASSIGNED
CTLO16==002000 ;DATA SET READY TRANSITION
CTLO20==001000 ;KMC-11 GOT A NXM ON A UNIBUS ADDRESS
CTLO22==000400 ;TRANSMIT UNDERRUN.
CTLO24==000200 ;RECEIVE OVERRUN
CTLO26==000100 ;BUFFER KILL COMPLETE
SUBTTL KDPONC -- ONCE ONLY ROUTINE FOR KMC/DUP
;THIS ROUTINE IS CALLED BY SYSINI. IT VERIFYS THAT ALL KMC-11 AND
; DUP-11 UNITS SPECIFIED BY MONGEN ACTUALLY EXIST.
KDPONC::PUSHJ P,SAVE2## ;HERE FROM ONCE. WE USE 2 P'S
MOVSI P1,(DF.RQK) ;JUST TO GET THINGS STARTED,
IORM P1,DEBUGF## ; REQUEST KDPLDR TO RUN NEXT (FIRST) TICK
SETZ P1, ;P1 IS CURRENT KDP INDEX, START WITH KDP ZERO
KDPOCP: CAILE P1,KDPMAX## ;LOOP OVER ALL KDP'S
POPJ P, ;IF WE'VE DONE THEM ALL, RETURN
SKIPN W,KDPTBL##(P1) ;GET THE ADDRESS OF THE NEXT KDP BLOCK
AOJA P1,KDPOCP ; IF IT'S NOT THERE, TRY THE NEXT ONE
MOVE T1,KDPCSR(W) ;GET THE ADDRESS OF THE KMC-11/KDP
PUSHJ P,UBGOOD## ;SEE IF IT EXISTS (UNIBUS TRAP/DOESN'T TRAP)
JRST KDPNXP ;NON-EXISTANT KMC/KDP
;NOW MAKE SURE IT'S NOT RUNNING (OTHERWISE IT WILL INTERRUPT BEFORE
; THE VECTORS ARE SET UP)
MOVEI T1,KMCMCL ;GET THE MASTER CLEAR BIT.
WRIO T1,@KDPCSR(W) ;STOP IT GOOD.
;NOW CHECK ALL LINES ON THE KDP
SETZ P2, ;START WITH LINE ZERO
KDPOCL: CAML P2,KDPDPN(W) ;LOOP OVER ALL LINES
AOJA P1,KDPOCP ;WHEN DONE ALL LINES, DO NEXT KDP
MOVEI F,KDPKDL(W) ;GET THE ADDRESS OF THE KDL TABLE
ADDI F,(P2) ;RELOCATE TO THIS LINE
SKIPN F,(F) ;GET ADDRESS OF KDL PAGE (SKIP IF VALID)
AOJA P2,KDPOCL ;DUP-11 PATCHED OUT. DO NEXT ONE.
PUSH P,KDLUSR(F) ;PRESERVE THE MONGEN LINE USER
HRLI T1,(F) ;GET FROM ADDRESS
HRRI T1,1(F) ;GET TO ADDRESS
SETZM (F) ;ZERO FIRST WORD AND
BLT T1,KDLLEN-1(F) ; BLT THE ENTIRE PAGE TO ZERO
POP P,KDLUSR(F) ;RESTORE LINE USER SO WE AUTO START
MOVEI T1,(P2) ;GET A COPY OF THE LINE NUMBER
LSH T1,3 ; * 8 TO GET DUP-11 CSR OFFSET
ADD T1,KDP1DP(W) ;RELOCATE TO THE ADDRESS OF KDP'S 1ST DUP
PUSHJ P,UBGOOD## ;SEE IF THE DUP-11 EXISTS.
JRST KDPNXL ;NON-EXISTANT DUP-11
AOJA P2,KDPOCL ;GO CHECK NEXT LINE.
;KDPNXP - ROUTINE TO DECLARE A KMC-11 NON-EXISTANT. UNIT IS MARKED
; NON-EXISTANT BY PUTTING A ZERO IN IT'S "KDPTBL" ENTRY.
KDPNXP: SETZM KDPTBL##(P1) ;CLEAR THE KDP 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 /? No KMC-11 for /
PUSHJ P,PRKDP ;TELL HIM WHICH KDP (FROM "W")
PUSHJ P,INLMES## ;FINISH THE MESSAGE
ASCIZ /.
/
AOJA P1,KDPOCP ;GO DO NEXT KDP
;KDPNXL - ROUTINE TO DECLARE A DUP-11 NON-EXISTANT. LINE IS MARKED
; NON-EXISTANT BY PUTTING A ZERO IN IT'S "KDPKDL" ENTRY.
KDPNXL: MOVEI T1,KDPKDL(W) ;GET THE ADDRESS OF THE KDL TABLE
ADDI T1,(P2) ;GET ADDRESS OF ENTRY FOR THIS LINE
SETZM (T1) ;CLEAR IT SO DUP-11 WON'T BE USED
MOVEM P2,KDLINE(F) ;SET LINE NUMBER IN PAGE FOR "PRKDL"
MOVE U,OPRLDB## ;GET THE ADDRESS OF THE OPRS TERMINAL
PUSHJ P,INLMES## ;TELL HIM THE FIRST PART OF THE BAD NEWS
ASCIZ /? No DUP-11 for /
PUSHJ P,PRKDL ;TELL HIM WHICH LINE
PUSHJ P,INLMES## ;FINISH THE LINE
ASCIZ /.
/
AOJA P2,KDPOCL ;STEP TO THE NEXT LINE
;KDPLDR ROUTINE TO START KDPLDR ON "FRCLIN"
;CALL PUSHJ P,KDPLDR## ;CALLED FROM CLOCK1
;RETURN POPJ ;ALWAYS
KDPLDR::SKIPGE DEBUGF## ;DON'T RUN IF DEBUGGING
POPJ P, ;THINGS ARE CONFUSING ENOUGH AS IT IS
PUSHJ P,FRCSET## ;SETUP TO TYPE ON FRCLIN
PUSHJ P,INLMES## ;TYPE THE COMMAND
ASCIZ /KDPLDR/
PJRST CRLF## ;TYPE THE CLOSING CRLF AND WE'RE DONE
SUBTTL KDPSEC - ONCE/SECOND PROCESSING
;KDPSEC THIS ROUTINE PERFORMS THE TIMING FUNCTIONS REQUIRED BY DDCMP
; IN PARTICULAR, IT SENDS
; STARTS WITH PERIOD "STRSEC"
; STACKS WITH PERIOD "STKSEC"
; REPS WITH PERIOD "REPSEC"
; IT ALSO COUNTS THE NUMBER OF "UN-ANSWERED" REPS, AND DECLARES THE
; LINE DOWN IF THIS COUNT EXCEEDS "REPMAX".
; IN THE EVENT THAT A LINE-DOWN EVENT DOES OCCUR, THE
; OPERATOR IS INFORMED THAT THE LINE "TIMED OUT"
KDPSEC::PUSHJ P,SAVEFW ;PRESERVE W & F
PUSHJ P,SAVE2## ;P1 := KDP COUNT, P1 := KDL
SETZ P1, ;START AT THE FIRST (0 TH) KDP
KDPSEP: CAILE P1,KDPMAX## ;IF WE HAVE DONE ALL KMC-11'S
POPJ P, ; THEN RETURN
SETZ P2, ;START AT LINE ZERO ON EACH KDP
SKIPE W,KDPTBL##(P1) ;IF THE KDP EXISTS, THEN
PUSHJ P,KDPSEL ; GO PROCESS IT'S LINES
AOJA P1,KDPSEP ;COUNT THE KDP AND TRY THE NEXT
KDPSEL: CAML P2,KDPDPN(W) ;IF WE HAVE PROCESSED ALL LINES ON THIS
POPJ P, ; KDP, THEN RETURN
MOVEI F,KDPKDL(W) ;GET ADDRESS OF THIS KDP'S KDL TABLE
ADD F,P2 ; AND CALCULATE ADDRESS OF CURRENT LINE
SKIPE F,(F) ;IF THE LINE EXISTS, THEN
PUSHJ P,KDPSE0 ; GO DO 1/SECOND PROCESSING ON IT
AOJA P2,KDPSEL ;LOOP OVER ALL LINES ON THE KDL
KDPSE0: AOS KDLZTM(F) ;INDICATE THAT WE'VE BEEN UP A SECOND LONGER
PUSHJ P,FREMAI ;FREE ANY STALE MAINTENANCE MESSAGES
KDPOFF ;WE BETTER BE CAREFULL WHEN IT'S RUNNING
LDB T1,KDLSTA## ;GET OUR STATE
SUBI T1,KD%STR ;ADJUST SO THAT "SENDING STARTS" IS STATE 0
JUMPL T1,KDPSE2 ; ANY STATE BEFORE DOESN'T GET TIMED
CAILE T1,KD%RUN-KD%STR ;[DBUG] JUST MAKE SURE IT'S A
PUSHJ P,NTDSTP## ;[DBUG] REASONABLE STATE
LDB T2,KDLTIM## ;GET THE TIMER
CAIN T2,KD%MXT ;STOPPED?
JRST KDPSE2 ;YES, IGNORE IT
AOS T2 ;COUNT UP ONE MORE SECOND
DPB T2,KDLTIM## ;PUT THE UPDATED TIME BACK
CAMGE T2,[EXP STRSEC,STKSEC,REPSEC](T1) ;HAS IT OVERFLOWED
JRST KDPSE2 ; IF NOT, JUST CALL NETSER
SETO T2, ;IF IT'S OVERFLOWED, STOP IT
DPB T2,KDLTIM## ; (NEXT EVENT WILL RESTART IT IF NECESSARY)
MOVE T2,[EXP KDSSTR,KDSSTK,KDSREP](T1) ;GET "REQUEST BIT" TO SET.
IORM T2,KDLSTS(F) ;REQUEST THE APROPRIATE CONTROL MESSAGE
TLNN T2,(KDSREP) ;WAS THIS A "REP"
JRST KDPSE1 ; IF NOT, SEND MESSAGE AND EXIT.
LDB T1,KDLRPC## ;IF IT'S A REP, GET THE REPCOUNT
AOS T1 ; AND ACCOUNT FOR THE COMING REP.
DPB T1,KDLRPC## ;STORE THE UPDATED COUNT BACK
CAIG T1,REPMAX ;HAVE WE "REP TIMED-OUT"?
JRST KDPSE1 ; IF NOT, JUST SEND THE REP AND EXIT
;FALL THROUGH TO DECLARE THE LINE DOWN
;(FROM ABOVE) LINE HAS TIMED OUT. RESTART IT AND TELL THE OPERATOR.
MOVEI T1,KD%STR ;GET THE "SENDING START'S STATE"
DPB T1,KDLSTA## ; AND PUT THIS LINE IN THAT STATE
MOVSI T1,(XMTBTS) ;GET MASK OF "MESSAGE QUEUED" BITS
ANDCAM T1,KDLSTS(F) ; AND CLEAR ALL PENDING REQUESTS
MOVSI T1,(KDSSTR) ;GET THE "SEND A START" REQUEST BIT
IORM T1,KDLSTS(F) ; AND REQUEST A START
PUSHJ P,KDLPDN ;TELL OWNER THAT THIS LINE IS RESTARTING.
KDPON ;ALLOW INTERRUPTS WHILE CALLING SCNSER
MOVE U,OPRLDB## ;WE WANT TO TELL THE OPR, GET HIS LDB ADDR
PUSHJ P,INLMES## ;SEND FIRST PART OF MESSAGE
ASCIZ /%% Synch line /
PUSHJ P,PRKDL ;PRINT THE FULL KDP#, KDL# MESSAGE
PUSHJ P,INLMES## ;NOW FINISH THE MESSAGE WITH .CRLF
ASCIZ / timed out.
/
KDPOFF ;NO INTERRUPTS FOR XMT/RCV BUF
KDPSE1: PUSHJ P,RCVBUF ;MAKE SURE WE'VE POSTED RECEIVE BUFFERS
PUSHJ P,XMTBUF ;TRY TO SEND THE MESSAGE
KDPSE2: KDPON ;INT'S OK NOW
POPJ P, ; AND WE'RE DONE
SUBTTL KDP. -- UUO INTERFACE TO THE KMC/DUP LINE UNIT.
;KDP.
;
;FUNCTIONS THAT AFFECT THE KMC AND/OR ALL DUP-11'S CONNECTED:
;
; KDP.KN ;RETURN THE NUMBER OF KMC-11'S ON SYS
; KDP.DN ;RETURN THE NUMBER OF DUP-11'S ON A KMC
; KDP.HA ;HALT THE KMC. ARG1 = KMC #
; KDP.MC ;MASTER CLEAR. ARG1 = KMC #
; KDP.ST ;START THE KMC. ARG1 = KMC #
; KDP.RE ;READ CRAM. ARG1 = KMC #,
; ; ARG2 = CRAM ADDRESS,
; ; ARG3 GETS CONTENTS OF CRAM
; KDP.WR ;WRITE CRAM. ARGS AS IN KDP.RE.
; KDP.KS ;GET THE STATUS OF THE KMC.
;
;FUNCTIONS THAT AFFECT JUST A SINGLE DUP-11:
;
; KDL.RS ;READ DUP-11'S STATUS REGISTERS
; KDL.HA ;HALT DDCMP ON THE LINE
; KDL.ST ;START DDCMP ON THE LINE
; KDL.SU ;SET THE LINE'S USER
;
;ERRORS
KE%ILF==ECOD1## ;ILLEGAL FUNCTION CODE
KE%ILK==ECOD2## ;ILLEGAL KDP NUMBER
KE%ALS==ECOD3## ;ARG LIST TOO SHORT
KE%IWR==ECOD4## ;ILLEGAL WHEN KMC-11 RUNNING
KE%ICA==ECOD5## ;ILLEGAL CRAM ADDRESS (READ OR WRITE)
KE%ILL==ECOD6## ;ILLEGAL LINE NUMBER
KE%KNR==ECOD7## ;KMC-11 NOT RUNNING (KDL TYPE OP'S)
KE%LNS==ECOD10## ;KDL LINE NOT STARTED
KE%LAS==ECOD11## ;KDL LINE ALREADY STARTED
KE%UNP==ECOD12## ;USER NOT PRIVILEGED
KE%IUN==ECOD13## ;ILLEGAL LINE USER NAME (SET LINE USER FCN)
KE%NDL==ECOD14## ;NO DDB FOR LINE WHILE TRYING TO START PROTOCOL
KDP.:: PUSHJ P,SAVE2## ;SAVE A COUPLE OF P'S
HLRZ P2,T1 ;SAVE THE LENGTH OF THE ARG LIST
CAIGE P2,2 ;MAKE SURE THAT THERE ARE AT LEAST 2 ARGS
PJRST KE%ALS ;ILLEGAL ARGUMENT LIST
HRR M,T1 ;SET RH(M) := ADDRESS OF ARG LIST
PUSHJ P,GETWDU## ;GET THE FUNCTION CODE
JUMPLE T1,KE%ILF ;ILLEGAL FUNCTION
CAILE T1,KDOPMX ;RANGE CHECK FOR KDP FUNCTIONS
JRST [CAILE T1,KLOPST;IF NOT KDP, SEE IF KDL FUNCTION
CAILE T1,KLOPMX; WHICH IS BETWEEN THESE TWO VALUES
PJRST KE%ILF ;ILLEGAL FUNCTION
JRST .+1] ;LEGAL FUNCTION. CONTINUE WITH MAIN FLOW
MOVEI P1,(T1) ;REMEMBER THE FUNCTION CODE FOR LATER
CAIG P1,1 ;IF IT'S A KDPOP THAT DOESN'T SPECIFY A KDP
JRST KDPOP. ; GO TO THE DISPATCH NOW
PUSHJ P,GETWD1## ;GET THE KMC-11 NUMBER
SKIPL T1 ;MAKE SURE THAT IT'S A
CAILE T1,KDPMAX## ; LEGAL KMC-11 NUMBER
PJRST KE%ILK ;EXIT IF ILLEGAL KMC-11 NUMBER
SKIPN W,KDPTBL##(T1) ;LOAD KDP POINTER
PJRST KE%ILK ;KMC MUST NOT EXIST (ONCE DIDN'T FIND IT)
;HERE IF THIS IS A KMC-11 (KDP) ORIENTED FUNCTION
; AT THIS POINT:
;
; P1 := KDP. FUNCTION CODE.
; P2 := LENGTH OF ARG-LIST.
KDPOP.: CAILE P1,KDOPMX ;IS THIS A KDP OR A KDL STYLE OP?
JRST KDLOP. ;THIS IS A KDL OP. USE DIFFERENT DISPATCH
JRST @.(P1) ;DISPATCH ON THE FUNCTION CODE
JRST KDPKN ; 1 = RETURN NUMBER OF KMC-11'S
JRST KDPDN ; 2 = RETURN NUMBER OF DUP=11'S
JRST KDPSS ; 3 = READ STATUS
JRST KDPHA ; 4 = HALT KMC-11
JRST KDPMC ; 5 = MASTER CLEAR THE KMC (DECLARE LINES DOWN)
JRST KDPST ; 6 = START THE KMC-11
JRST KDPRE ; 7 = READ A KMC-11 CRAM LOCATION
JRST KDPWR ; 8 = WRITE A KMC-11 CRAM LOCATION
KDOPMX==8 ;MAXIMUM KDP FUNCTION CODE
;HERE IF THIS IS A DUP-11 (KDL) ORIENTED FUNCTION
; AT THIS POINT:
;
; P1 := KDP. FUNCTION CODE.
; P2 := LENGTH OF ARG-LIST.
KDLOP.: CAIGE P2,3 ;MAKE SURE THERE IS A THIRD ARG (LINE #)
PJRST KE%ALS ; IF NOT, GIVE ARG LIST TOO SHORT ERROR
PUSHJ P,GETWD1## ;GET THE DUP-11 NUMBER
CAML T1,KDPDPN(W) ;MAKE SURE IT'S IN RANGE
PJRST KE%ILL ; IF NOT, GIVE "ILLEGAL LINE NUMBER" ERROR
MOVEI F,KDPKDL(W) ;GET ADDRESS OF POINTERS TO KDL BLOCKS
ADDI F,(T1) ;ADD OFFSET TO PROPER LINE
SKIPN F,(F) ;LOAD ADDRESS OF KDL BLOCK
PJRST KE%ILL ;DUP-11 DOES NOT EXIST. (ONCE DIDN'T FIND IT)
KLOPTB: JRST @.-KLOPST(P1) ;DISPATCH ON THE FUNCTION CODE
KLOPST==100 ;LINE OP'S START AT "100"
JRST KDLRS ; 1 = READ LINE'S STATUS
JRST KDLHA ; 2 = HALT THE LINE (STOP DDCMP)
JRST KDLST ; 3 = START THE LINE (CONTROL OUT) START DDCMP
JRST KDLSU ; 4 = SET LINE'S USER (ANF, DECnet, PROGRAM)
JRST KDLRU ; 5 = READ LINE'S USER IN SIXBIT
KLOPMX==KLOPST+.-KLOPTB ;MAX OP CODE ALLOWED
SUBTTL KDP. SUB-FUNCTIONS (KDPOP.)
;HERE ARE THE VARIOUS KMC-11 (KDP) ORIENTED FUNCTION
; AT THIS POINT:
;
; P1 := KDP. FUNCTION CODE.
; P2 := LENGTH OF ARG-LIST.
; #1 -- RETURN THE NUMBER OF KDP'S ON THE SYSTEM
KDPKN: MOVEI T1,KDPMAX## ;GET NUMBER OF THE LAST ONE
ADDI T1,1 ;INCREMENT IT TO GET A COUNT
PUSHJ P,PUTWD1## ;RETURN THE NUMBER IN ARG #2
RETSKP ;GIVE GOOD RETURN
; #2 -- RETURN THE NUMBER OF DUP-11'S ON A PARTICULAR KDP
KDPDN: MOVE T1,KDPDPN(W) ;GET THE NUMBER OF DUP-11'S ON THIS KDP
PUSHJ P,PUTWD1## ;RETURN THE NUMBER IN ARG #3
RETSKP ;GIVE GOOD RETURN
; #3 -- RETURN THE STATUS WORD OF KDP BLOCK
KDPSS: MOVE T1,KDPSTS(W) ;GET THE STATUS WORD
PUSHJ P,PUTWD1## ;RETURN STATUS WORD
RETSKP ;GIVE GOOD RETURN
; #4 -- HALT THE KMC-11. THIS DOES NOT DECLARE DUP-11 LINES DOWN SINCE
; SETTING THE RUN BIT WOULD, IN THEORY, ALLOW THE KMC-11 TO CONTINUE.
KDPHA: PUSHJ P,NTDPRV## ;THIS IS A PRIVILEGED FUNCTION
PJRST KE%UNP ;IF NO PRIVILEGES, GIVE ERROR RETURN
MOVEI T1,0 ;GET THE KMC-11 RUN BIT
WRIO T1,@KDPCSR(W) ;CLEAR THE RUN/MAINT BITS
MOVSI T1,(KDPSRU) ;GET THE "MICROCODE RUNNING" BIT
ANDCAM T1,KDPSTS(W) ; AND CLEAR THAT
RETSKP ;GIVE GOOD RETURN
; #5 -- MASTER CLEAR THE KMC-11. THIS DECLARES THE DUP-11 LINES DOWN SINCE
; THERE IS NOW NO WAY TO CONTINUE DDCMP ON THEM.
KDPMC: PUSHJ P,NTDPRV## ;THIS IS A PRIVILEGED FUNCTION
PJRST KE%UNP ;IF NO PRIVILEGES, GIVE ERROR RETURN
PUSHJ P,KDPHLT ;LET "HALT" DO THE WORK
RETSKP ;GIVE GOOD RETURN
; #6 -- START THE KMC-11. THIS FIRST STARTS THE KMC-11, AND THEN
; SETS UP THE INTERRUPT VECTORS. ONCE THE VECTORS ARE SET UP,
; IT SETS THE TWO KMC-11 INTERRUPT ENABLES, AND DECLARES THE KDP RUNNING.
KDPST: PUSHJ P,NTDPRV## ;THIS IS A PRIVILEGED FUNCTION
PJRST KE%UNP ;IF NO PRIVILEGES, GIVE ERROR RETURN
MOVEI T1,KMCRUN ;GET THE RUN BIT
TIOE T1,@KDPCSR(W) ;MAKE SURE THE KMC-11 ISN'T RUNNING ALREADY
PJRST KE%IWR ;IF IT IS, GIVE "ILLEGAL WHEN RUNNING" ERROR
WRIO T1,@KDPCSR(W) ;SET RUN NOW, WILL SET IEI & IEO SOON
; (MUST GIVE KMC TIME TO INIT)
MOVE T1,KDPVEC(W) ;GET THE ADDRESS OF THE VECTOR
LSH T1,-2 ;CONVERT BYTE OFFSET INTO WORD OFFSET
MOVSI T2,(XPCW) ;GET BODY OF A "XPCW" INSTRUCTION
HRRI T2,KDPIVA(W) ;GET ADDRESS OF VECTOR "A" INTERRUPT SERVICE
MOVEM T2,VECTB3##(T1) ;STORE ADDRESS OF ROUTINE TO FORCE JSR TO
HRRI T2,KDPIVB(W) ;GET ADDRESS OF "B" SERVICE ROUTINE
MOVEM T2,VECTB3##+1(T1);SET UP VECTOR "B" INTERRUPT SERVICE
MOVEI T1,KMCRUN!KMCIEI!KMCIEO ;START THE KMC-11 AND ENABLE INTS
WRIO T1,@KDPCSR(W) ;START THE KMC-11
MOVSI T1,(KDPSRU) ;GET AND SET THE "RUNNING"
IORB T1,KDPSTS(W) ; BIT SO IT AT LEAST LOOKS LIKE WE'RE UP.
RETSKP ;ALL DONE. GIVE THE USER A GOOD RETURN
; #7 -- READ A KMC-11 CRAM LOCATION. KMC-11 MUST BE HALTED
KDPRE: CAIGE P2,4 ;MAKE SURE THE ARG LIST IS LONG ENOUGH
PJRST KE%ALS ;IF ARG LIST IS TO SHORT, GIVE AN ERROR
MOVE P1,KDPCSR(W) ;GET THE ADDRESS OF THE CSR FOR THE KMC-11
MOVEI T1,KMCRUN ;GET THE RUN BIT
TIOE T1,SEL0(P1) ;SEE IF THE KMC-11 IS RUNNING
PJRST KE%IWR ;IF IT IS RUNNING, GIVE AN ERROR
MOVEI T1,KMCRMO ;GET THE "READ CRAM" BIT
WRIO T1,SEL0(P1) ;ENABLE THE KMC-11 FOR MAINT CRAM READ
PUSHJ P,GETWD1## ;GET THE ARG WHICH IS THE CRAM ADDRESS
CAIL T1,0 ;RANGE CHECK THE CRAM ADDRESS
CAIL T1,CRAMSZ ; TO BE SURE IT FITS IN THE CRAM
PJRST KE%ICA ;GIVE AN ILLEGAL CRAM ADDR IF IT'S OUT
WRIO T1,SEL4(P1) ;STORE THE ADDRESS IN MAINT CRAM ADDR REG
RDIO T1,SEL6(P1) ;READ THE CRAM LOCATION
PUSHJ P,PUTWD1## ;STORE THE RESULT IN ARG #4
PJRST KDPHA ;CLEAR KDPSRU AND EXIT
; #8 -- WRITE A KMC-11 CRAM LOCATION. JUST LIKE READ (ALMOST)
KDPWR: CAIGE P2,4 ;MAKE SURE THE ARG LIST IS LONG ENOUGH
PJRST KE%ALS ;IF ARG LIST IS TO SHORT, GIVE AN ERROR
MOVE P1,KDPCSR(W) ;GET THE ADDRESS OF THE CSR FOR THE KMC-11
MOVEI T1,KMCRUN ;GET THE RUN BIT
TIOE T1,SEL0(P1) ;SEE IF THE KMC-11 IS RUNNING
PJRST KE%IWR ;IF IT IS RUNNING, GIVE AN ERROR
MOVEI T1,KMCRMO ;GET THE "READ CRAM" BIT
WRIO T1,SEL0(P1) ;ENABLE THE KMC-11 FOR MAINT CRAM READ
PUSHJ P,GETWD1## ;GET THE ARG WHICH IS THE CRAM ADDRESS
CAIL T1,0 ;RANGE CHECK THE CRAM ADDRESS
CAIL T1,CRAMSZ ; TO BE SURE IT FITS IN THE CRAM
PJRST KE%ICA ;GIVE AN ILLEGAL CRAM ADDR IF IT'S OUT
WRIO T1,SEL4(P1) ;STORE THE ADDRESS IN MAINT CRAM ADDR REG
PUSHJ P,GETWD1## ;GET THE VALUE TO WRITE
WRIO T1,SEL6(P1) ;PUT IT IN THE CRAM MEMORY BUFFER REGISTER
MOVEI T1,KMCCWR ;GET THE CRAM WRITE BIT
BSIO T1,SEL0(P1) ;CLOCK THE DATA INTO THE CRAM
PJRST KDPHA ;CLEAR KDPSRU AND EXIT
SUBTTL KDLOP. SUB-FUNCTIONS.
;THESE ARE THE DUP-11 (KDL) ORIENTED KDP. SUB-FUNCTIONS.
; AT THIS POINT:
;
; T1 := THE DUP-11'S LINE NUMBER (NEEDED FOR "START")
; P1 := KDP. FUNCTION CODE.
; P2 := LENGTH OF THE ARGLIST
; #101 RETURN THE LINE STATUS BLOCK. RETURNS ALL COUNTERS ETC...
KDLRS: CAIGE P2,4 ;SKIP IF ARG LIST CONTAINS "XWD LEN,ADDR"
PJRST KE%ALS ;ARG LIST TOO SHORT IF NOT THERE
PUSHJ P,GETWD1## ;GET "XWD LENGTH,ADDRESS" OF RETURN VAL AREA
MOVE P2,T1 ;COPY IT TO A SAFER PLACE
HLRZ T2,P2 ;GET THE LENGTH OF THE RETURN VALUE AREA
CAIGE T2,<KDLEST-KDLSTS>+1 ;SEE IF IT WILL HOLD THE DATA
PJRST KE%ALS ;GIVE ARGLIST TOO SHORT ERROR IF WON'T FIT
MOVEI T1,(P2) ;GET USER ADDRESS OF RETURN AREA
ADDI T2,-1(T1) ;GET ADDRESS OF LAST WORD IN AREA
PUSHJ P,TRNGE## ;MAKE SURE IT'S ALL IN CORE.
MOVEI T1,(P2) ;GET ADDRESS OF RETURN AREA BACK
MOVEI T2,<KDLEST-KDLSTS>+1 ;GET LENGTH OF STATUS BLOCK
ADDI T2,-1(T1) ;GET ADDRESS OF LAST WORD TO WRITE
HRLI T1,KDLSTS(F) ;GET ADDRESS OF FIRST STATUS WORD TO SEND
EXCTXU <BLT T1,(T2)> ;COPY THE BLOCK INTO USER MEMORY
RETSKP ;GIVE GOOD RETURN
; #102 HALT A SINGLE DUP-11 LINE. TERMINATES DDCMP AND DECLARES THE
; LINE DOWN.
KDLHA: MOVE T1,KDPSTS(W) ;GET THE KMC-11 STATUS
TLNN T1,(KDPSRU) ;IF IT'S NOT RUNNING
PJRST KE%KNR ; GIVE A "KDP NOT RUNNING" ERROR
PUSHJ P,NTDPRV## ;THIS IS A PRIVILEGED FUNCTION
PJRST KE%UNP ;IF NO PRIVILEGES, GIVE ERROR RETURN
LDB T1,KDLSTA## ;GET THE LINES STATE
CAIG T1,KD%FLS ;MAKE SURE WE THINK THAT THE LINE'S UP
PJRST KE%LNS ; IF NOT, GIVE "LINE NOT STARTED" ERROR
PJRST KF.HA ;HALT THE LINE
; #103 ROUTINE TO START DDCMP ON A SPECIFIED DUP-11 LINE.
;NOTE: T1 STILL HAS THE LINE'S NUMBER IN IT!!!
KDLST: MOVE T1,KDPSTS(W) ;GET THE KMC-11 STATUS
TLNN T1,(KDPSRU) ;IF IT'S NOT RUNNING
PJRST KE%KNR ; GIVE A "KDP NOT RUNNING" ERROR
PUSHJ P,NTDPRV## ;THIS IS A PRIVILEGED FUNCTION
PJRST KE%UNP ;IF NO PRIVILEGES, GIVE ERROR RETURN
LDB T2,KDLSTA## ;GET OUR CURRENT STATE
CAIL T2,KD%FLS ;SEE IF WE ARE IN A STATE TO RESTART
PJRST KE%LAS ; IF RUNNING, SAY "LINE ALREADY STARTED"
HRRZ T2,KDLUSR(F) ;GET THE LINE'S USER'S ID
CAIN T2,DD.PRO ; AND IF IT'S NOT "PROGRAM"
SKIPE KDLDDB(F) ; OR IF HE HAS A DDB,
CAIA ; THEN HE'S OK
PJRST KE%NDL ;NO DDB FOR LINE IN PROGRAM MODE
LDB T1,KDLSTA## ;GET THE STATE,
CAIL T1,KD%INI ; AND IF WE'VE BEEN INITIALIZED
JRST KDLST1 ;DON'T DO IT AGAIN
PUSHJ P,KDLINI ;INITIALIZE THE LINE
PUSHJ P,ZRCTRS ;ZERO THE STATISTIC COUNTERS
;NOW SEE WHAT MODE THE USER WANT'S TO START THE LINE IN
KDLST1: SETZ T1, ;ASSUME NORMAL MODE
CAIL P2,4 ;IF THE USER SPECIFIED A FORTH ARG,
PUSHJ P,GETWD1## ; THEN GO FETCH THE TYPE
MOVEI T2,KD%MAI ;ASSUME HE MENT TO START MAINT MODE
JUMPN T1,KDLST2 ; AND IF SO, GO DO IT
MOVSI T1,(XMTBTS) ;MAKE SURE WE LEAVE NO CONTROL MESSAGES
ANDCAM T1,KDLSTS(F) ;PENDING FROM LAST TIME
MOVSI T1,(KDSSTR) ;GET THE "NEED TO SEND START" BIT
IORM T1,KDLSTS(F) ; AND SET IT SO WE SEND ONE SOON
PUSHJ P,CLCTRS ;MAKE SURE WE START CORRECTLY
MOVEI T2,KD%STR ;GET THE "SENDING STARTS" STATE
KDLST2: DPB T2,KDLSTA## ;SET THIS LINE'S STATE
KDPOFF ;NO INT'S IN RCV/XMT-BUF
PUSHJ P,RCVBUF ;SET UP A RECEIVE REQUEST
PUSHJ P,XMTBUF ;TRY TO SEND THE START
KDPON ;INT'S OK NOW
RETSKP ;GIVE GOOD RETURN
; #104 ROUTINE TO SET THE LINE'S USER
KDLSU: PUSHJ P,NTDPRV## ;MUST BE PRIVILEGED TO TO THIS
PJRST KE%UNP ;IF HE'S NOT, THEN GIVE AN ERROR
CAIGE P2,3 ;MAKE SURE THAT THE ARG LIST IS LONG ENOUGH
PJRST KE%ALS ;IF NOT, SAY "ARG LIST TOO SHORT"
LDB T1,KDLSTA## ;GET THE LINE'S CURRENT STATE
CAIL T1,KD%FLS ;THE LINE MUST BE HALTED TO SWITCH USERS
PJRST KE%LAS ;SAY "LINE STARTED" IF IT IS RUNNING
PUSHJ P,GETWD1## ;GET THE "NAME" OF THE USER TO SWITCH TO
KDLSU1: PUSHJ P,KONUSN## ;IDENTIFY USER NAME
JRST KE%IUN ; THEN SAY ILLEGAL USER NAME
HRRZM T2,KDLUSR(F) ;IF IT MATCHES, SET THE LINE TYPE
CAIN T2,DD.DEC ;IS THE USER DECNET?
JRST KDLSUD ;YES, START UP ROUTER
CAIN T2,DD.PRO ;NO, IS IT PROGRAM?
HRLM J,KDLUSR(F) ;YES, SAVE THE JOB NUMBER
RETSKP ;NO, RETURN SUCCESS
;Now that we know we are setting the user to DECNET, we'll set the
;line's state to on, so that when the protocol comes up RTR will be
;ready. Note that if the definitions of the LINE ID change,
;this crock will have to change.
KDLSUD: SETZ T4, ;START OFF WITH A CLEAN LINE ID
MOVE T2,KDPNUM(W) ;GET THE KMC NUMBER (USUALLY ZERO)
DPB T2,[POINT 9,T4,17] ;SAVE IT AS KONTROLLER NUMBER (LI.KON)
MOVE T2,KDPDPN(W) ;GET THE NUMBER OF KDPS ON THIS KMC
MOVEI T3,KDPKDL(W) ;POINT TO KDL TABLE
SOJGE T2,[CAMN F,(T3) ;DO WE HAVE A MATCH
JRST KDLSD1 ;YES, THAT MEANS WE FOUND KDP NUMBER
AOJA T3,.] ;NO, ADVANCE TO NEXT AND CHECK IT OUT
PUSHJ P,NTDSTP ;OOPS, SEVERE PROBLEM
KDLSD1: SUBI T3,KDPKDL(W) ;FIND THE KDP NUMBER
DPB T3,[POINT 9,T4,26] ;SAVE IT AS UNIT NUMBER (LI.UNI)
MOVEI T2,LD.KDP ;THIS MEANS THE KDP DEVICE TO RTR
DPB T2,[POINT 9,T4,8] ;SAVE AS DEVICE TYPE (LI.DEV)
MOVEI T1,DC.IOC ;INTERRUPT TO OPEN A CIRCUIT
TLO T4,(LILXC) ;SET THE BIT TO MAKE IT A LINE ID
MOVE T3,T4 ;GET LINE ID IN PROPER AC
PUSHJ P,CALDVR ;CALL THE DRIVER
RETSKP ;GIVE A GOOD RETURN
; #105 ROUTINE TO READ THE LINE'S USER
KDLRU: CAIGE P2,3 ;MAKE SURE ARG LIST IS LONG ENOUGH
PJRST KE%ALS ;IF NOT, SAY "ARG LIST TOO SHORT"
HRRZ T1,KDLUSR(F) ;GET THE USER OF THE LINE
MOVE T1,DTNAME##(T1) ;GET THE SIXBIT NAME
PUSHJ P,PUTWD1## ;PLACE THE VALUE IN USER BLOCK
HLRZ T1,KDLUSR(F) ;GET THE JOB NUMBER OF THE OWNER
; (ZERO IF NOT DD.PRO)
PUSHJ P,PUTWD1## ;PLACE IT IN NEXT WORD OF USER BLOCK
RETSKP ; AND GIVE A GOOD RETURN
SUBTTL KDPDSP - ENTRY TO KDPINT
;KDPDSP - THIS ROUTINE IS THE OUTSIDE WORLD'S ENTRY INTO KDPINT.
;CALL MOVX T1,FUNCTION-CODE (DC.F??)
; MOVX T2,KDL-BLOCK ADDRESS
; T3, T4 ARE ARGUMENTS FOR FUNCTION
;PUSHJ P,KDPDSP
;RETURN CPOPJ ;IF CALLED WHEN OWNED BY SOMEONE ELSE
; CPOPJ1 ;ON SUCCESS
;FOR DECNET
KDPDSP::SKIPL T1 ;RANGE CHECK
CAILE T1,DC.FAL ; THE FUNCTION CODE [DC.FMX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := KDL, W := KDP
CAIN T1,DC.FAL ;ASSIGN LINE?
JRST KF.AL ;YES
MOVE F,T2 ;SET UP OUR KDL BLOCK POINTER
HRRZ T2,KDLUSR(F) ;GET THE USER OF THE LINE
SKIPE KDLLBK(F) ;BETTER HAVE A DECNET BLOCK
CAIE T2,DD.DEC ;DECNET?
POPJ P, ;NOPE, GIVE ERROR RETURN.
HRRZ W,KDLKDP(F) ;SET UP THE KDP ADDRESS AS WELL
PUSHJ P,@KDPDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;FAILURE
PJRST CPOPJ1## ;GIVE GOOD RETURN
;FOR ANF
KDADSP::SKIPL T1 ;RANGE CHECK
CAILE T1,DC.FQB ; THE FUNCTION CODE [DC.FMX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := KDL, W := KDP
CAIN T1,DC.FAL ;ASSIGN LINE?
JRST KF.AL ;YES
MOVE F,T2 ;SET UP OUR KDL BLOCK POINTER
HRRZ T2,KDLUSR(F) ;GET THE USER OF THE LINE
CAIE T2,DD.ANF ;ANF?
POPJ P, ;NOPE, GIVE ERROR RETURN.
HRRZ W,KDLKDP(F) ;SET UP THE KDP ADDRESS AS WELL
PUSHJ P,@KDPDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;FAILURE
PJRST CPOPJ1## ;GIVE GOOD RETURN
;FOR PROGRAM
KDODSP::SKIPL T1 ;RANGE CHECK
CAILE T1,DC.FQB ; THE FUNCTION CODE [DC.FMX]
PUSHJ P,NTDSTP## ;IF OUT OF RANGE, STOP
PUSHJ P,SAVEFW ;WE USE F := KDL, W := KDP
CAIN T1,DC.FAL ;ASSIGN LINE?
JRST KF.AL ;YES
MOVE F,T2 ;SET UP OUR KDL BLOCK POINTER
HRRZ T2,KDLUSR(F) ;GET THE USER OF THE LINE
CAIE T2,DD.PRO ;DECNET?
POPJ P, ;NOPE, GIVE ERROR RETURN.
HRRZ W,KDLKDP(F) ;SET UP THE KDP ADDRESS AS WELL
PUSHJ P,@KDPDST(T1) ;DISPATCH ON THE FUNCTION CODE
POPJ P, ;FAILURE
PJRST CPOPJ1## ;GIVE GOOD RETURN
KDPDST: 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
;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)
CAILE W,KDPMAX## ;BETTER BE IN RANGE
POPJ P, ;SIGH
SKIPN W,KDPTBL##(W) ;POINT TO THE KDP BLOCK
POPJ P, ;PATCHED OUT
MOVE T1,KDPCSR(W) ;GET UNIBUS ADDRESS
PUSHJ P,UBGOOD## ;BE SURE WE'RE THERE
POPJ P, ;WE'RE NOT
LDB F,[POINT 9,T2,26] ;GET UNIT (DUP) NUMBER (LIUNI)
CAML F,KDPDPN(W) ;LEGAL NUMBER
POPJ P, ;NO
ADDI F,KDPKDL(W) ;POINT TO KDL POINTER
SKIPN F,0(F) ;SEE IF PRESENT
POPJ P, ;PATCHED OUT
LDB T1,[POINT 9,T2,26] ;GET UNIT (DUP) NUMBER (LIUNI) AGAIN
LSH T1,3 ; * 8 TO GET DUP-11 CSR OFFSET
ADD T1,KDP1DP(W) ;RELOCATE TO THE ADDRESS OF KDP'S 1ST DUP
PUSHJ P,UBGOOD## ;SEE IF THE DUP-11 EXISTS.
POPJ P, ;IT DOESN'T, DON'T INIT
HRRZ T2,KDLUSR(F) ;GET LINE OWNER
CAIE T2,DD.DEC ;HACKING?
POPJ P, ;NOT ME, YOU DON'T
MOVEM T3,KDLLBK(F) ;SAVE ADDRESS OF DECNET'S BLOCK
MOVE T1,F ;RETURN ADDRESS OF OURS
JRST CPOPJ1## ;SUCCESS
;KF.HA - ROUTINE TO SHUT-DOWN PROTOCOL ON A KDL-LINE.
;CALL MOVX F,KDL-BLOCK ADDRESS
; PUSHJ P,KF.HA ;FROM EITHER CLOCK OR UUO LEVEL WITH THE
; ; INTERRUPTS ON.
;RETURN POPJ P, ;ALWAYS - HALTS KMC IF ERROR
KF.HA: LDB T1,KDLSTA## ;FIRST GET OUR STATE,
CAIGE T1,KD%FLS ;IF WE ARE ALREADY "HALTED"
RETSKP ;RETURN WITH OUT TOUCHING ANYTHING
; PUSHJ P,KDLFLS ;MUST DO A BUFFER FLUSH TO QUIESCE THE KMC
MOVEI T1,KD%INI ;FLUSH COMPLETE, NOW GO
DPB T1,KDLSTA## ;TO "INITIALIZED" STATE
PUSHJ P,KDLPDN ;TELL OUR DRIVER THAT WE ARE DOWN
RETSKP ;AND THE "HALT" COMMAND IS DONE
;KF.IN - ROUTINE TO INITIALIZE THE KDL AND PUT IT IN NORMAL PROTOCOL
;CALL MOVX F,KDL-BLOCK ADDRESS
; PUSHJ P,KF.IN
;RETURN CPOPJ ;ALWAYS
KF.IN: PUSHJ P,FREMAI ;FREE BUFFERS IF IN MAINT STATE
LDB T1,KDLSTA## ;GET THE LINE'S STATE
CAIGE T1,KD%INI ;IF THE LINE NEEDS { BASE ! CONTROL } IN
PUSHJ P,KDLINI ;THEN INITIALIZE THE LINE
PUSHJ P,KDLPD0 ;CLEAN OUT QUEUES.
MOVEI T1,KD%STR ;GET THE "SENDING STARTS" STATE
DPB T1,KDLSTA## ; AND SET THIS LINE'S STATE TO IT
MOVSI T1,(XMTBTS) ;MAKE SURE WE LEAVE NO CONTROL MESSAGES
ANDCAM T1,KDLSTS(F) ;PENDING FROM LAST TIME
MOVSI T1,(KDSSTR) ;GET THE "NEED TO SEND START" BIT
IORM T1,KDLSTS(F) ; AND SET IT SO WE SEND ONE SOON
PUSHJ P,CLCTRS ;CLEAR THE MESSAGE NUMBERS
KDPOFF ;NO INT'S IN RCV/XMT-BUF
PUSHJ P,RCVBUF ;SET UP A RECEIVE REQUEST
PUSHJ P,XMTBUF ;TRY TO SEND THE START
KDPON ;INT'S OK NOW
RETSKP ;AND WE'RE DONE
;KF.MA - ROUTINE TO INITIALIZE MAINT PROTOCOL ON THE KDL-LINE
;CALL MOVX F,KDL-BLOCK
; PUSHJ P,KF.MA
;RETURN POPJ P, ;ALWAYS
KF.MA: LDB T1,KDLSTA## ;FIRST MAKE GET OUR STATE, AND
CAIN T1,KD%MAI ; IF WE ARE ALREADY IN MAINT STATE,
RETSKP ; THEN DON'T DO ANYTHING MORE
CAILE T1,KD%MAI ;IF WE ARE RUNNING (OR TRYING TO START)
PUSHJ P,KF.HA ; THEN FLUSH ALL BUFFERS
JFCL ;IGNORE ERROR RETURN
LDB T1,KDLSTA## ;GET OUR STATE AGAIN
CAIGE T1,KD%INI ;IF THE LINE NEEDS INITIALIZATION,
PUSHJ P,KDLINI ;THEN RE-SETUP THE KDL-BLOCK
MOVEI T1,KD%MAI ;GET AND SET THIS LINE TO
DPB T1,KDLSTA## ; MAINT STATE
KDPOFF ;DISABLE INTERRUPTS WHILE WE
PUSHJ P,RCVBUF ;SET UP RECEIVE AND
PUSHJ P,XMTBUF ; TRANSMIT REQUESTS
KDPON ;INTERUPTS ON AGAIN
RETSKP ;AND WE'RE DONE
;KF.QO - ROUTINE TO QUEUE OUTPUT MESSAGES
;CALL MOVX F,KDL-BLOCK ADDRESS
; MOVX T3,MESSAGE BUFFER ADDRESS
; PUSHJ P,KF.QO
;RETURN CPOPJ ;ALWAYS
KF.QO: HLLZS MB.NXT(T3) ;ZERO THE FORWARD POINTER
SETOM MB.MSN(T3) ;-1 IN NUMBER FIELD MEANS NO NUMBER ASSIGNED
KDPOFF ;PREPARE TO MESS WITH THE QUEUES
LDB T1,KDLSTA## ;GET STATE
CAIE T1,KD%RUN ;ARE WE IN STILL RUN STATE?
CAIN T1,KD%MAI ;IT'S OK TO SEND DATA IN MAINT MODE TOO
CAIA ;GOODNESS, IT'S OK
JRST [KDPON ;NO, PASS PTR TO MSG IN T3
MOVEI T1,DC.IOF ; GET THE "OUTPUT NOT DONE" FUNCTION
PUSHJ P,CALDVR ; AND TELL OUR DRIVER THE NEWS
RETSKP] ;AND RETURN NO ERROR
HRRZ T1,KDLWTO(F) ;GET THE FIRST MESSAGE ON THE "WAIT OUTPUT" Q
JUMPE T1,[HRRZM T3,KDLWTO(F) ;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
KDPON ;DONE MESSING
PUSHJ P,FREMAI ;TRY TO FREE STALE MAINT MSG
RETSKP ;DONE
SUBTTL INTERRUPTS -- INTERRUPT LEVEL INTERFACE TO THE KMC-11
Comment @
Each KMC-11 has two interrupt vector addresses.
"A" This interrupt is taken when RDYI (KMCRDI) comes up. In this
state the KMC-11 is ready for an input transaction. All
input transactions for the KMC-11 are queued in the KDP block.
This is necessary because the following situation would otherwise
cause a deadlock.
1) The KMC-11 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 KMC-11 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 above problem.
"B" This interrupt is taken when RDYO (KMCRDO) comes up. In this
state the KMC-11 is wants to perform an output transaction.
It is these output transactions that drive almost all of the
interrupt level KMC/DUP processing.
The vector instructions are set up to be JSR's to the locations "KDPAIV",
and "KDPBIV" in the KDP block for the KMC-11. These locations contain the
5 or 6 instructions necessary to save the AC's, load "W" with the address
of the particular KDP block, and dispatch to either of the two interrupt
routines "KDPAIV" or "KDPBIV".
End comment @
SUBTTL KDPAIV -- KMC-11 INTERRUPT VECTOR "A" PROCESSING.
;KDPAIV -- ROUTINE TO HANDLE KMC-11 INTERRUPT VECTOR "A" (INPUT)
;CALL MOVE W,[EXP KDP-BLOCK-ADDRESS]
; PUSHJ P,KDPAIV ;CALLED FROM KDPIVA IN THE KDP 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 KDP INTERLOCK
;
KDPAIV::AOS KDPACT(W) ;COUNT THE INTERRUPT
MOVE T1,KDPSTS(W) ;GET THE STATUS
TLNN T1,(KDPSRU) ; AND MAKE SURE WE THINK WE'RE ON LINE
PJSP T1,KDPERR ; IF WE DON'T, CLEAR "RUN" SO INTS WILL STOP
MOVE U,KDPCSR(W) ;GET THE UNIBUS ADDRESS OF THE KMC-11
MOVEI T1,KMCRDI ;GET THE "RDYI" FLAG
TION T1,SEL2(U) ;MAKE SURE "RDYI" IS UP
PJSP T1,KDPERR ; IF IT'S NOT, THEN ITS AN ILLEGAL INTERRUPT
MOVE T3,KDPIQT(W) ;GET NUMBER OF NEXT QUEUED TRANSACTION
CAMN T3,KDPIQP(W) ;MAKE SURE THAT IT'S DIFFERENT NOT THE "PUTTER"
PJSP T1,KDPERR ; IF IT IS, THEN WE'RE GETTINT UNSOLICITED
; INTERRUPTS. DECLARE KDP ILL.
LSH T3,1 ;MAKE IT AN OFFSET INTO THE QUEUE
ADDI T3,KDPINQ(W) ;RELOCATE BY THE ADDRESS OF THE QUEUE
MOVE T1,0(T3) ;GET SEL2 DATA
MOVE T2,1(T3) ;GET SEL4, SEL6 DATA
AOS T3,KDPIQT(W) ;ADVANCE QUEUE TAKER
CAIL T3,KDPQLN ;IF ITS TIME TO WRAP AROUND, THEN
SETZB T3,KDPIQT(W) ; WRAP AROUND TO THE FIRST ENTRY
MOVEI T4,KMCRQI ;GET THE REQUEST INPUT INTERRUPT BIT
CAMN T3,KDPIQP(W) ;IF QUEUE EMPTY (PUTTER = TAKER) THEN
BCIO T4,SEL0(U) ; CLEAR RQI TO STOP THE INTERRUPTS
WRIO T2,SEL6(U) ;STORE TOP WORD
MOVSS T2,T2 ;GET SEL4 DATA
WRIO T2,SEL4(U) ; AND STORE THAT
TRZ T1,KMCRDI ;MAKE SURE RDYI CLEAR (SO KMC CAN RUN)
WRIO T1,SEL2(U) ;GIVE REST OF DATA TO KMC
POPJ P, ;ALL DONE
;KDPBIV -- ROUTINE TO HANDLE KMC-11 INTERRUPT VECTOR "B" (OUTPUT)
;CALL MOVE W,[EXP KDP-BLOCK-ADDRESS]
; PUSHJ P,KDPBIV ;CALLED FROM KDPIVB IN THE KDP BLOCK
;RETURN POPJ P, ;TO DISMISS THE INTERRUPT
;
;CLOBBERS MOST AC'S (WE SHOULD HAVE OUR OWN AC BLOCK ANYWAYS)
;
;ON MULTI-PROCESSOR SYSTEMS THIS CODE WILL NEED TO AOSE THE KDP INTERLOCK.
;
KDPBIV::AOS KDPBCT(W) ;COUNT THE INTERRUPT
MOVE T1,KDPSTS(W) ;GET OUR PERCEIVED STATUS
TLNN T1,(KDPSRU) ; AND MAKE SURE WE THINK WE'RE RUNNING
PJSP T1,KDPERR ; IF WE'RE NOT, CLEAR RUN AND RETURN
MOVE U,KDPCSR(W) ;GET THE UNIBUS ADDRESS OF THE KMC
RDIO P1,SEL2(U) ;READ STATUS BITS
TRNN P1,KMCRDO ;BETTER WANT AN OUTPUT TRANSACTION
PJSP T1,KDPERR ;ILLEGAL INTERRUPT. CRASH KMC
RDIO P2,SEL4(U) ;READ BDL ADDRESS
RDIO P3,SEL6(U) ;READ ERROR, STATUS OR WHAT EVER
MOVEI T1,KMCRDO ;GET THE RDYO BIT
BCIO T1,SEL2(U) ;CLEAR IT TO LET THE KMC CONTINUE
TRNE P1,2!KMCOVR ;MAKE SURE TYPE IS RIGHT, AND NO OVERRUN
PJSP T1,KDPERR ; WE'RE CONFUSED. GIVE UP.
LDB F,[POINT 7,P1,27] ;GET 7 BIT LINE #.
CAML F,KDPDPN(W) ;IS THIS A LEGAL LINE FOR THIS KDP
PJSP T1,KDPERR ; IF NOT, BETTER STOP NOW...
ADDI F,KDPKDL(W) ;MAKE IT AN OFFSET INTO THE KDP BLOCK
HRRZ F,(F) ;LOAD APPRIOATE KDL BLOCK ADDRESS
SETZ T1, ;GET A ZERO REGISTER
TRNE P1,KMCIOT ;IF INPUT, THEN
TRO T1,1 ; SET LOW ORDER BIT
TRNE P1,1 ;IF CONTROL, THEN
TRO T1,2 ; SET NEXT TO LOW ORDER BIT
JRST @[JRST KDPBOO ;BUFFER OUT (OUTPUT)
JRST KDPBOI ;BUFFER OUT (INPUT)
JRST KDPCOO ;CONTROL OUT (OUTPUT)
JRST KDPCOI](T1);CONTROL OUT (INPUT)
;ROUTINE DISPATCHED TO WILL RETURN.
SUBTTL KDPBOO -- KMC-11 BUFFER-OUT(OUTPUT) TRANSACTION PROCESSING.
; This routine frees output buffers when a BUFFER-OUT(OUTPUT) transaction
;declares that the KMC/DUP has output all data in the buffer. It:
; 1) Makes sure that this is the last buffer in the current
; Buffer Description List. (Data messages consist of two
; buffers. The DDCMP header buffer, and the data buffer.
; These two buffers are combined in a single BDL) If
; the current buffer is not the last in the list, nothing
; more is done, and the interrupt is exited. (We will
; get another interrupt when the next buffer in the BDL
; finishes.)
; 2) The count of buffers outstanding is decremented, and a
; check is made to ensure that this buffer is really the
; next one we expected to finish.
; 3) XMTBUF is called in an attempt to fill the just freed output
; buffer.
;
;called with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPBOO: PUSHJ P,GETBDL ;GET T4 SET UP TO POINT TO BDL
TLNE T4,1 ;MAKE SURE THAT BUFFER-OUT STARTS ON EVEN BYTE
PJSP T1,KDPERR ; IF ODD BYTE, THEN KMC-11 SCREWED US
MOVE T1,1(T4) ;GET WORD WITH LAST BD HALFWORD
TLNN T4,2 ;IF WE WANT FIRST HALFWORD,
MOVS T1,T1 ; THEN SWAP IT TO THE LOW HALF
TRNN T1,BDLLDS ;IS THIS THE LAST BUFFER IN THE BDL
POPJ P, ;IF NOT, IGNORE THIS INTERRUPT.
;WE ARE AT END OF MESSAGE. MAKE SURE WE GOT THE RIGHT ONE BACK
MOVEI S,KDSNXB ;STATUS BIT THAT SAYS WHICH BUFFER IS NEXT OUT
MOVEI T1,KDLXD1(F) ;ASSUME THAT BUFFER #1 IS FIRST BACK
TDNE S,KDLSTS(F) ; BUT IF IT SHOULD BE BUFFER #2, THEN
MOVEI T1,KDLXD2(F) ; THEN CHANGE OUR MIND.
MOVSI T2,(1B0) ;GET THE BDL IN-USE MARK BIT
TDNN T2,0(T1) ; AND MAKE SURE THAT IT'S SET
;[DBUG] PJSP T1,KDLERR ;IF NOT, TRY TO CRASH THE LINE
PUSHJ P,NTDSTP## ;WE'VE SCREWED UP THE BUFFERS [DBUG]
ANDCAM T2,0(T1) ;CLEAR THE USE BIT
HLRZ T3,1(T1) ;GET THE HALFWORD CONTAINING THE 3RD WORD
; OF THE FIRST BD IN THE BDL
TRNN T3,BDLLDS ;IF IT'S NOT THE LAST BD, THEN
ADD T1,[XWD 2,1] ; ADVANCE THE CBP IN T1 3 WORDS (6 BYTES)
CAMN T1,T4 ;MAKE SURE IT'S THE BDL ADDRESS THE KMC GAVE US
SOSGE KDLXBC(F) ;DECREMENT THE NUMBER OF ACTIVE OUTPUT BUFFERS
PJSP T1,KDPERR ;++ EITHER BAD BUFFER, OF COUNT WENT NEGATIVE.
XORB S,KDLSTS(F) ;SIGNAL THAT OTHER BUFFER IS NEXT BUFFER OUT
PJRST XMTBUF ;NOW GO TRY TO FILL THE JUST FREED BUFFER
SUBTTL KDPBOI -- ROUTINE TO HANDLE BUFFER-OUT(INPUT) TRANSACTIONS.
; This routine handles BUFFER-OUT(INPUT) 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 KMC 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
; 2) Calls RCVMSG to process the incoming message
;
;called with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPBOI: PUSHJ P,FREBOI ;FREE THE INPUT BUFFER, (SET UP T4)
PJSP T1,KDPERR ;?? KMC-11 GAVE BAD BDL ADDRESS ??
PJRST RCVMSG ;GO PROCESS MESSAGE (T4 POINTS TO BDL)
SUBTTL KDPCOO -- PROCESS A CONTROL-OUT(OUTPUT) TRANSACTION
; This routine processes CONTROL-OUT(OUTPUT) transactions. These
;consist primarily of various errors detected by the KMC-11. This routine:
; 1) Counts the control out transaction code (CTOTLY)
; 2) Verifys that the error is legal/recoverable. If not,
; it crashes the KMC-11.
; 3) If the control out frees an output BDL, it assumes that
; an output message has been clobbered, queues a REP message
; to speed a recovery NAK, and calls "KDPBOO" to free the BDL.
;
;called with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPCOO: PUSHJ P,SAVE1## ;WE USE P1 FOR A "BIT MASK"
PUSHJ P,CTOTLY ;TALLY THE CONTROL OUT CODE AND
; PUT "BIT MASK" IN P1
PJSP T1,KDPERR ;IF ILLEGAL CODE, KMC-11 IS SICK...
TLNE P1,^-<CTLO14!CTLO16!CTLO22!CTLO26> ;THESE ARE LEGAL OUTPUT
PJSP T1,KDPERR ; IF IT'S NOT ONE OF THEM, CRASH KMC-11
TLNE P1,CTLO14 ;IS THIS A BUFFER TEMP UNAVAILABLE?
JRST [MOVEI T1,RSNBTU ;IF SO, GET THAT NAK CODE
PJRST RCVXNK] ;SEND THE NAK AND RETURN
TLNE P1,CTLO26 ;IF THIS A "FLUSH DONE" TRANSACTION
JRST KDPFLO ; IF FLUSH DONE, GO UPDATE KDLSTS
TLNN P1,CTLO22 ;DOES THIS TRANSACTION FREE A BDL?
POPJ P, ; IF NOT, RETURN (DON'T CALL XMTBUF...)
MOVSI T1,(KDSREP) ;ASSUME MESSAGE WAS CLOBBERED, SO
IORM T1,KDLSTS(F) ; SO REQUEST A REP TO SPEED RECOVERY
PJRST KDPBOO ;PROCESS REST JUST LIKE ANY OTHER BUFFER-OUT
SUBTTL KDPCOI -- PROCESS CONTROL-OUT(INPUT) TRANSACTIONS
; This routine processes the CONTROL-OUT(INPUT) transactions. These
;transactions are primarily errors noticed by the KMC-11. In particular,
;BCC (checksum) errors are processed here. This routine:
; 1) Tallys the CONTROL-OUT transaction
; 2) Sees if it is a legal/recoverable error. If not, it
; crashes the KMC-11
; 3) If the transaction frees an input buffer, that is done.
; 4) If the transaction implies that a message was lost,
; an approiate NAK is queued.
;
;called with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
;
KDPCOI: PUSHJ P,SAVE1## ;P1 WILL HAVE THE "BIT MASK" IN IT.
PUSHJ P,CTOTLY ;TALLY THE CONTROL OUT TYPE
; RETURNS "BIT MASK" IN P1
PJSP T1,KDPERR ;UNKNOWN CODE. KMC-11 IS SICK...
TLNE P1,^-<CTLO10!CTLO12!CTLO24!CTLO26> ;IS THIS LEGAL FOR INPUT
PJSP T1,KDLERR ;UN-RECOVERABLE ERROR. BETTER START OVER
TLNE P1,CTLO26 ;IS THIS AN "INPUT FLUSH" DONE TRANSACTION
JRST KDPFLI ; IF INPUT FLUSH, GO UPDATE KDLSTS
TLNE P1,<CTLO10!CTLO12!CTLO24> ;DOES THIS FREE A BDL?
PUSHJ P,[PUSHJ P,FREBOI ;FREE THE INPUT BDL
PJSP T1,KDLERR ;BAD ADDRESS RETURNED BY KMC-11. DIE
POPJ P,] ;RETURN TO MAIN FLOW
TLNN P1,<CTLO10!CTLO12!CTLO24> ;DOES THIS REQUIRE A NAK?
PJRST RCVBUF ;IF NO NAK REQUIRED, WE ARE DONE
; ATTEMPT TO REQUEUE A BUFFER AND RETURN
MOVEI T1,RSNHCK ;ASSUME THAT ERROR WAS HEADER CHECKSUM
TLNE P1,CTLO12 ; BUT IF IT WAS A DATA CHECKSUM
MOVEI T1,RSNDCK ; THEN CHANGE OUR MINDS AND USE THAT
TLNE P1,CTLO24 ; UNLESS IT WAS AN OVER-RUN, IN WHICH
MOVEI T1,RSNOVR ; CASE WE SHOULD USE THIS.
PJRST RCVXNK ;QUEUE NAK AND REQUEUE BUFFERS IF POSSIBLE
;KDPFLO ROUTINE TO CLEAN UP WHEN OUTPUT BUFFER FLUSH IS COMPLETED.
; CLEAR "XMIT FLUSH IN PROGRESS" AND SAY NO OUTPUT BUFFERS QUEUED.
KDPFLO: LDB T1,KDLSTA## ;GET THE LINE'S STATE (JUST TO MAKE SURE)
MOVEI T2,KDSXFL ;GET THE "XMIT FLUSH" IN PROGRESS BIT
CAIN T1,KD%FLS ;IF WE'RE NOT IN "BUFFER FLUSH STATE", OR
TDNN T2,KDLSTS(F) ; WE'RE NOT FLUSHING XMIT BUFFERS
PJSP T1,KDPERR ; THEN ASSUME THE KMC-11 SCREWED UP
SETZM KDLXBC(F) ;SAY "NO OUTPUT BUFFERS ACTIVE"
MOVEI T1,KDSNXB!KDSXFL; ALSO SAY THAT NEXT BUFFER IS #0, AND
ANDCAB T1,KDLSTS(F) ; WE'RE NOT FLUSHING ANY MORE
JRST KDPFLX ;SEE IF WE CAN LEAVE "FLUSH" STATE
;KDPFLI ROUTINE TO CLEAN UP WHEN INPUT BUFFER FLUSH IS COMPLETE
KDPFLI: LDB T1,KDLSTA## ;GET LINE'S STATE
MOVEI T2,KDSRFL ;"RECEIVE FLUSH IN PROGRESS" BIT
CAIN T1,KD%FLS ;MAKE SURE WE'RE FLUSHING
TDNN T2,KDLSTS(F) ;MAKE SURE IT'S INPUT
PJSP T1,KDPERR ;IF NOT INPUT FLUSH, THEN KMC GAVE BAD CODE
SETZM KDLRBC(F) ;ZERO COUNT OF RECEIVE BUFFERS POSTED
MOVEI T1,KDSNRB!KDSRFL;SAY BUFFER #0 NEXT, AND
ANDCAB T1,KDLSTS(F) ; INPUT FLUSH COMPLETE
KDPFLX: MOVEI T2,KD%INI ;ASSUME THAT WE HAVE CLEARED BOTH FLUSH BITS
TRNN T1,KDSRFL!KDSXFL; AND IF WE HAVE,
DPB T2,KDLSTA## ; THEN WE'RE IN "INITED" STATE
POPJ P, ;DONE WITH THE INTERRUPT
SUBTTL RCVMSG -- ROUTINE TO DISPATCH ON THE DDCMP MESSAGE TYPE.
;Called from KDPBOI with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVMSG: ADDI T4,3 ;POINT TO BUFFER 3 PDP-10 WORDS AFTER BDL
HRLI T4,2 ;DDCMP HEADER STARTS IN RH SO DATA IS ON A WORD
LDB T1,BYTABL+2 ;GET FIRST CHAR FROM MESSAGE
CAIN T1,SOH ;SOH ==: DATA MESSAGE
JRST RCVDAT ; IF DATA, GO PROCESS IT
CAIN T1,ENQ ;ENQ ==: CONTROL MESSAGE
JRST RCVCTL ; IF CONTROL GO PROCESS IT
CAIN T1,DLE ;DLE ==: MAINTENANCE MESSAGE.
JRST RCVMAI ; IF MAINT, GO PROCESS THAT
PJSP T1,KDPERR ;IF WE DON'T RECOGNIZE IT, ASSUME KMC IS ILL.
SUBTTL RCVMAI -- ROUTINE TO PROCESS INCOMING MAINTENANCE MESSAGES.
;Called from RCVMSG with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVMAI: AOS KDLMAR(F) ;COUNT THE MAINTENANCE MESSAGES RECEIVED
LDB T1,KDLSTA## ;GET THE LINE'S STATE
CAIG T1,KD%MAI ; AND SEE IF WERE IN MAINT STATE ALREADY
JRST RCVMA1 ;IF NOT UP-AND-RUNNING, DON'T TELL OPERATOR
CAIL T1,KD%RUN ;IF THIS LINE IS UP, THEN
PUSHJ P,KDLPDN ; TELL OUR USER THAT IT'S NOT!
MOVE U,OPRLDB## ;LINE JUST CRASHED. TELL OPERATOR
PUSHJ P,INLMES## ; ABOUT THIS
ASCIZ /%% Maintenance message received on /
PUSHJ P,PRKDL ;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.
RCVMA1: LDB T1,KDLSTA## ;GET OUR STATE, AND
MOVEI T2,KD%MAI ;GET THE MAINTENANCE STATE
DPB T2,KDLSTA## ; AND SET THIS LINE TO IT.
CAILE T1,KD%MAI ;IF WE ARE TRYING TO START OR RUNNING PROTOCOL
PUSHJ P,[PUSH P,T4 ;SAVE CURRENT BDL POINTER
PUSHJ P,KDLPDN ;FREE OUR BUFFERS, TELL USER
MOVEI T1,DC.IMR ;THEN TELL OUR DRIVER THAT
PUSHJ P,CALDVR
POP P,T4 ;RESTORE THE BDL POINTER
POPJ P,] ;AND RETURN
LDB T1,KDLSTA## ;GET OUR (POSSIBLY CHANGED!!) STATE
CAIE T1,KD%MAI ;AND IF OUR DRIVER HASN'T LEFT US IN MAINT
PJRST RCVBUF ; STATE - DON'T READ THE MESSAGE
TRNN P3,BFREOM ;DOES THIS BUFFER HOLD THE ENTIRE MESSAGE
PJSP T1,KDLERR ; IF NOT, WE CAN'T HANDLE IT. DECLARE ERROR.
LDB T3,BYTABL+3 ;GET THE LOW 8 BITS OF THE LENGTH
LDB T2,BYTABL+4 ;GET THE HIGH 6 BITS
DPB T2,[POINT 6,T3,27] ;MAKE A 12 BIT NUMBER FROM THEM
JUMPE T3,RCVBUF ;IF EMPTY MESSAGE, IGNORE IT
PUSH P,T3 ;SAVE THE COUNT
PUSH P,T4 ; AND THE CBP TO THE MESSAGE
MOVEI T1,DC.IGB ;REQUEST AN INPUT BUFFER
PUSHJ P,CALDVR ;CALL THE DRIVER FOR A BUFFER
POP P,T4 ;RESTORE THE CBP
POP P,T3 ; AND THE LENGTH OF THE MESSAGE
JUMPE T1,RCVBUF ;IF NO FREE BUFFERS, IGNORE THE MESSAGE
ADD T4,[XWD 1,1] ;ADVANCE THE INPUT BYTE POINTER TO START OF MSG
;(HEADER IS 6 BYTES, WE WANT PRE-DEC CBP)
MOVE T2,MB.FMS(T1) ;GET THE POINTER TO THE MSD (IF DECNET)
JUMPE T2,[MOVSI T2,(POINT 8) ;IF ANF STYLE, BUILD A BYTE
HRRI T2,STCDAT(T1) ; POINTER TO THE DATA AREA
JRST RCVMA2] ; AND GO COPY THE DATA
CAMLE T3,MD.BYT(T2) ;FIRST MAKE SURE THE WE GOT A BIG ENOUGH BUFFER
PUSHJ P,NTDSTP## ;++ DRIVER GAVE US A BAD 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 TO THE FIRST BYTE
RCVMA2: IBP T2 ;POINT TO WORD CONTAINING FIRST BYTE
MOVEI T2,@T2 ;RESOLVE ANY INDEXING, ETC
HRLI T2,1(T4) ;POINT TO FIRST DATA WORD OF MESSAGE
SOS T3 ;ROUND BYTE COUNT AND CONVERT TO WDS (+3 -4)
LSH T3,-2 ;BYTES - WORDS
ADDI T3,(T2) ;END ADDRESS
BLTUB T2,(T3) ;MOVE MESSAGE TRANSLATED. (MABYE FEW EXTRA)
MOVE T3,T1 ;COPY THE ADDRESS OF THE BUFFER
MOVEI T1,DC.IIC ;GET THE "INPUT DONE" FUNCTION
PUSHJ P,CALDVR ;TELL THE DRIVER THAT THE BUFFER'S FULL
PJRST RCVBUF ; AND WE'RE DONE
SUBTTL RCVCTL -- ROUTINE TO PROCESS INCOMING CONTROL MESSAGES.
; This routine dispatches on the type field of incoming DDCMP
;control messages.
;
;Called from RCVMSG with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVCTL: TRNN P3,BFREOM ;WAS THIS MESSAGE LONGER THAN A SINGLE BUFFER
PJSP T1,KDPERR ; IF IT WAS, THEN THE KMC-11 SCREWED UP...
LDB T3,KDLSTA## ;GET THE LINES STATE
CAIG T3,KD%FLS ;IF LINE IS NOT CURRENTLY ACTIVE,
POPJ P, ; THEN RETURN WITH OUT RE-QUEUEING A RCV RQST
LDB T1,BYTABL+3 ;GET THE SECOND BYTE (CONTROL TYPE)
; CAIL T1,%ACK ;RANGE CHECK IT
CAILE T1,%STACK ; TO BE SURE THAT IT'S A LEGAL MESSAGE
PJSP T1,KDPERR ;THE KMC FED US GARBAGE...
MOVEI T2,KDLCTR-1(F) ;GET A POINTER TO THE CONTROL MESSAGE COUNTS
ADDI T2,(T1) ; OFFSET BY THE MESSAGE TYPE
AOS (T2) ; AND COUNT THIS RECEIVED CONTROL MESSAGE
JRST .(T1) ;DISPATCH TO APPROIATE MESSAGE PROCESSOR
JRST RCVACK ; 1 = ACK
JRST RCVNAK ; 2 = NAK
JRST RCVREP ; 3 = REP
JSP T1,KDPERR ; 4 = RESET (VESTIGIAL MESSAGE, NOW ILLEGAL)
JSP T1,KDPERR ; 5 = RESET (VESTIGIAL MESSAGE, NOW ILLEGAL)
JRST RCVSTR ; 6 = START
JRST RCVSTK ; 7 = STACK
RCVACK: PUSHJ P,CHKACK ;PROCESS ACK FIELD (MAY CALL DRIVER)
PJRST RCVBUF ;TRY TO SET UP ANOTHER RECEIVE REQUEST
; AND EXIT THE INTERRUPT.
RCVNAK: LDB T1,BYTABL+4 ;GET THE NAK REASON
ANDI T1,77 ; AND JUST THE REASON
PUSHJ P,NAKTRN ;CONVERT THE NAK CODE INTO A TABLE INDEX
ADDI T1,KDLNKR(F) ;RELOCATE TO RECEIVED NAK TABLE
AOS (T1) ;COUNT THE RECEIVED NAK
LDB T1,KDLSTA## ;GET OUR STATE, AND
CAIE T1,KD%RUN ; IF WE AREN'T RUNNING
PJRST RCVBUF ;DON'T PROCESS ANY NAK'S
PUSHJ P,CHKACK ;PROCESS THE ACK FIELD
PUSHJ P,STPTMR ;STOP THE TIMER
HRRZ T2,KDLWTA(F) ;GET THE HEAD OF THE LIST OF PCB'S AWAITING
; AN ACK
JUMPE T2,RCVNK2 ;IF NONE, SKIP OVER CODE THAT RESENDS THEM
RCVNK1: MOVEI T1,(T2) ;COPY THE ADDRESS OF THE "NEXT" PCB
HRRZ T2,MB.NXT(T2) ;GET THE POINTER TO THE REST OF THE LIST
JUMPN T2,RCVNK1 ;KEEP LOOPING IF NOT AT END OF LIST.
; AT EXIT, T1 := POINTER TO LAST PCB TO
; RE-TRANSMIT
HRRZ T2,KDLWTO(F) ;GET THE HEAD OF THE OUTPUT WAIT QUEUE
HRRM T2,PCBBLK(T1) ;AND MAKE IT THE "REST" OF THE WAIT ACK LIST
SETZ T1, ;GET A "ZERO" AND USE IT TO
EXCH T1,KDLWTA(F) ; CLEAR THE "WAIT FOR ACK" QUEUE.
MOVEM T1,KDLWTO(F) ;NOW MAKE THE OLD ACK QUEUE THE WAIT FOR OUTPUT
; QUEUE
SETZM KDLCTA(F) ;SAY THAT THERE ARE NO MESSAGES AWAITING ACK.
RCVNK2: PUSHJ P,XMTBUF ;SEE IF THE TRANSMITTER NEEDS POKING
; (IT WILL IF OUTPUT QUEUE WAS EMPTY)
PJRST RCVBUF ;GO SET UP A RECEIVE REQUEST AND EXIT.
RCVREP: LDB T1,KDLSTA## ;GET LINE STATE
CAIE T1,KD%RUN ;MUST IGNORE UNLESS RUNNING
PJRST RCVBUF ;IGNORE, REQUEUE RECEIVE BUFFER
LDB T1,KDLRMN## ;GET THE LAST RECIEVED MESSAGE NUMBER
LDB T2,BYTABL+6 ;GET THE "N" FIELD OF THE REP MESSAGE
CAIE T1,(T2) ;COMPARE MESSAGE NUMBERS. SKIP IF ALL OK
JRST [MOVEI T1,RSNREP ;IF WE MISSED SOME, GET CODE "REP RESPONSE"
PJRST RCVXNK] ;AND GO SEND A NAK
MOVSI S,(KDSACK) ;ASSUME RANDOM REP (RESPOND WITH ACK)
IORM S,KDLSTS(F) ;SET THE NEED TO SEND "?" MESSAGE FLAG
MOVSI S,(KDSNAK) ;CLEAR ANY PENDING NAK
ANDCAM S,KDLSTS(F) ;BECAUSE IT WOULD BE POINTLESS NOW
PJRST RCVNK2
RCVSTR: CAIE T3,KD%MAI ;IF NOT IN MAINT MODE
JRST RCVST1 ;SEE IF WE'RE IN "RUN"
MOVEI T1,DC.ISR ;IF IN MAINT MODE, SAY WE'VE RECEIVED
PUSHJ P,CALDVR ; A START MESSAGE
JRST RCVST2 ;AND GO SEE IF WE CAN PROCESS IT
RCVST1: CAIGE T3,KD%RUN ;ARE WE RUNNING NOW?
JRST RCVST2 ; IF NOT, DON'T WORRY ABOUT TELLING NETSER
PUSHJ P,KDLPDN ;TELL NETSER THAT THIS LINE IS RESTARTING.
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,PRKDL ;PRINT THE FULL KDP#, KDL# MESSAGE
PUSHJ P,INLMES## ;FINISH MESSAGE OFF WITH TIME OF DAY
ASCIZ / at /
PUSHJ P,PRDTIM## ;PRINT TIME AND CRLF
;DDCMP SAYS WE SHOULD JUST HALT. BUT WE WANT TO TRY AND KEEP THE
;LINE UP. SO WE ENTER SENDING STARTS STATE. WE CAN NOT SKIP TO
;SENDING STACKS, AS ANF PDP-11S GET VERY CONFUSED.
MOVEI T1,KD%STR ;SEND STARTS
DPB T1,KDLSTA## ;STARTING NOW
MOVSI T1,(XMTBTS) ;MAKE SURE PAST QUEUED REQUESTS ARE CANCELED
ANDCAM T1,KDLSTS(F) ;AS WE'D ONLY GET CONFUSED LATER
MOVSI T1,(KDSSTR) ;SEND A START
IORM T1,KDLSTS(F) ;WHEN WE GET A CHANGE
PJRST RCVNK2 ;CRANK UP THE KMC
RCVST2: LDB T1,KDLSTA## ;GET OUR STATE, AND
CAIE T1,KD%STR ; IF WE'RE NOT SENDING STARTS,
CAIN T1,KD%STK ; OR SENDING STACKS
CAIA ;OK
PJRST RCVBUF ;THEN DON'T PROCESS THE START
MOVEI T1,KD%STK ;GET THE "SENDING STACK'S STATE"
DPB T1,KDLSTA## ; AND PUT THIS LINE IN THAT STATE
MOVSI T1,(XMTBTS) ;GET MASK OF "MESSAGE QUEUED" BITS
ANDCAM T1,KDLSTS(F) ; AND CLEAR ALL PENDING REQUESTS
MOVSI T1,(KDSSTK) ;GET THE "SEND A STACK" REQUEST BIT
IORM T1,KDLSTS(F) ; AND REQUEST A STACK
PJRST RCVNK2 ;CRANK US UP
RCVSTK: CAIE T3,KD%RUN ;IF NOT RUNNING
JRST RCVSK1 ;PROCEED
MOVSI T1,(KDSACK) ;RUNNING, SEND AN ACK
IORM T1,KDLSTS(F) ;TO KEEP OTHER END HAPPY
MOVSI T1,(KDSNAK) ;DON'T ALLOW NAK
ANDCAM T1,KDLSTS(F) ;WHICH WOULD KEEP THE ACK FROM GOING OUT
JRST RCVNK2 ;CRANK THINGS UP
RCVSK1: CAIE T3,KD%STR ;IF WE AREN'T SENDING STARTS, THEN
CAIN T3,KD%STK ; OK IF SENDING STACKS TOO
CAIA
PJRST RCVBUF ; IGNORE ALL STACKS
PUSHJ P,STPTMR ;STOP THE TIMER
MOVEI T1,KD%RUN ;GET THE "RUN" STATE
DPB T1,KDLSTA## ; AND PUT THIS LINE IN "RUN"
MOVSI T1,(XMTBTS) ;GET THE BIT MASK OF MSG REQUESTS
ANDCAM T1,KDLSTS(F) ; AND CLEAR ALL QUEUED REQUESTS
MOVSI T1,(KDSACK) ;GET THE "REQUEST ACK" BIT,
IORM T1,KDLSTS(F) ; AND QUEUE AN ACK TO GET THINGS STARTED
PUSHJ P,KDLPUP ;TELL NETSER THAT THIS KDL IS ONLINE
JRST RCVNK2 ;CRANK THINGS UP
SUBTTL RCVDAT -- ROUTINE TO PROCESS INCOMING DATA MESSAGES.
;Called from RCVMSG with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11
; T4 := a CBP to the first byte of the BDL (set up by FREBOI)
;
RCVDAT: AOS KDLDTR(F) ;COUNT THIS RECEIVED DATA MESSAGE
TRNN P3,BFREOM ;DOES THIS BUFFER CONTAIN THE ENTIRE MESSAGE
PJSP T1,KDLERR ;IF NOT, WE'RE NOT GOING TO RECOVER
;SHOULD GIVE A "MESSAGE TO BIG" NAK BUT
; THE SECOND HALF OF THE MESSAGE WOULD
; BE INTERPRETED AS A COMPLETE NEW MESSAGE.
PUSH P,T4 ;PRESERVE OUR BDL POINTER
PUSHJ P,CHKACK ;PROCESS THE IMPLICIT ACK IN THE MESSAGE
POP P,T4 ;GET OUR BUFFER POINTER BACK
LDB T1,KDLSTA## ;FETCH OUR STATE
CAIE T1,KD%RUN ;IF WE ARE NOT RUNNINT,
PJRST RCVBUF ;THEN DON'T RECEIVE ANY DATA
LDB T1,BYTABL+6 ;GET THIS MESSAGES NUMBER
LDB T2,KDLRMN## ;GET THE NUMBER OF THE LAST MESSAGE RECEIVED
AOS T2 ; PLUS ONE
ANDI T2,377 ;MASK TO 8 BITS
CAIE T1,(T2) ;IS THIS THE ONE WE'RE EXPECTING?
PJRST RCVBUF ;IF NOT, JUST POST ANOTHER RECEIVE BUFFER
LDB T3,BYTABL+3 ;FIRST GET THE LOW 8 BITS
LDB T1,BYTABL+4 ; AND THEN THE UPPER 6
DPB T1,[POINT 6,T3,27] ;ASSEMBLE THEM INTO THE MESSAGE LENGTH
JUMPE T3,RCVBUF ;IF ZERO LENGTH MESSAGE, IGNORE
PUSH P,T2 ;REMEMBER MESSAGE NUMBER
PUSH P,T3 ;SAVE THE MESSAGE LENGTH
PUSH P,T4 ; AND OUR POINTER INTO THE INPUT BUFFER
MOVEI T1,DC.IGB ;GET THE "REQUEST INPUT BUFFER" FUNCTION
PUSHJ P,CALDVR ; AND CALL OUR DRIVER
POP P,T4 ;RESTORE BHT CBP
POP P,T3 ; AND THE MESSAGE LENGTH
POP P,T2 ;RESTORE OUR MESSAGE NUMBER
JUMPE T1,[MOVEI T1,RSNBTU ;IF WE DIDN'T GET A BUFFER
PJRST RCVXNK] ; SEND A "NO ROOM" NAK
DPB T2,KDLRMN## ;FINALLY. NOW WE CAN INCREMENT THE "RMN"
;NOW FIGURE OUT WHAT SORT OF INPUT BUFFER WE ARE COPYING INTO
PUSH P,T1 ;SAVE OUR POINTER TO THE BUFFER
MOVE T2,MB.FMS(T1) ;GET THE POINTER TO THE MSD (IF DECNET)
JUMPE T2,RCVDA1 ;IF ANY, GO GET POINTERS FROM PCB
; CAMLE T3,MD.ALL(T2) ;MAKE SURE THAT THE DATA WILL FIT
; PUSHJ P,NTDSTP## ;++ DRIVER 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) ; AND FETCH THE BYTE POINTER
JRST RCVDA2 ;NOW GO COPY THE DATA
;HERE FOR ANF-10 STYLE BUFFERS
RCVDA1: MOVE T2,PCBCTR(T1) ;GET THE SIZE OF THE PCB
CAMLE T3,T2 ; AND MAKE A QUICK SIZE CHECK
PUSHJ P,NTDSTP## ;STOP IF DRIVER GAVE TOO SMALL A BUFFER
MOVEM T3,PCBCTR(T1) ;STORE THE ACTUAL LENGTH FOR NETSER
MOVE T2,PCBPTR(T1) ;GET THE INPUT BYTE POINTER
; JRST RCVDA2 ;FALL THROUGH TO THE COPY ROUTINES
;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: ADD T4,[XWD 1,1] ;ADVANCE THE CBP TO THE FIRST DATA BYTE
;(DDCMP HEADER IS 6 BYTES, WANT PRE DEC CBP)
IBP T2 ;POINT TO WORD CONTAINING FIRST BYTE
MOVEI T2,@T2 ;RESOLVE ANY INDEXING, ETC
HRLI T2,1(T4) ;POINT TO FIRST DATA WORD OF MESSAGE
SOS T3 ;ROUND BYTE COUNT AND CONVERT TO WDS (+3 -4)
LSH T3,-2 ;BYTES - WORDS
ADDI T3,(T2) ;END ADDRESS
BLTUB T2,(T3) ;MOVE MESSAGE TRANSLATED. (MAYBE FEW EXTRA)
RCVDA6: POP P,T3 ;GET OUR BUFFER ADDRESS BACK
MOVEI T1,DC.IIC ;GET THE "INPUT DONE" FUNCTION CODE
PUSHJ P,CALDVR ;CALL THE DRIVER TO RETURN THE FILLED BUFFER
PUSHJ P,RCVBUF ;TRY TO QUEUE ANOTHER RECEIVE BUFFER
MOVSI T1,(KDSNAK) ;NO POINT ANY MORE
ANDCAM T1,KDLSTS(F) ;...
MOVSI T1,(KDSACK) ;SEND AN ACK WHEN XMITTER IS IDLE
IORM T1,KDLSTS(F) ;TO ALLOW REMOTE TO FREEE BUFFERS & FILL PIPE
PJRST XMTBUF ;ANY DATA MESSAGE 'IMPLIED ACK' WILL CANCEL
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) Constructs a BDL for the buffer.
; 4) Queue's a BUFFER-INPUT(INPUT) transaction for the KMC-11
;
;Called from "many places" with:
; F, W := KDL and KDP pointers
;
RCVBUF: LDB T1,KDLSTA## ;GET OUR STATE
CAIG T1,KD%FLS ;SKIP IF WE CAN QUEUE ANOTHER RECEIVE BUFFER
POPJ P, ; IF WE CAN'T, JUST EXIT NOW
AOSG T1,KDLRBC(F) ;INCREMENT COUNT OF RCV BUFFERS QUEUED
PUSHJ P,NTDSTP## ;++ SHOULD NEVER BE NEGATIVE
CAILE T1,2 ;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
JRST [SOS KDLRBC(F) ;CORRECT THE COUNT
POPJ P,] ; AND RETURN
PUSHJ P,SAVP4## ;BEFORE WE DO REAL WORK, PRESERVE PERM AC
SOS T1 ;T1 NOW IS EITHER 0 ! 1
MOVE T2,KDLSTS(F) ;GET THE STATUS (CONTAINS NEXT BUFFER OUT BIT)
TRNE T2,KDSNRB ;IF NEXT BUFFER OUT ISN'T 0
XORI T1,1 ;THEN NEXT BUFFER IN IS DIFFERENT
MOVEI P4,@[CAI KDLRD1(F) ;LOAD PROPER BDL ADDRESS
CAI KDLRD2(F)](T1) ;FROM T1
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 (WE SET IT LATER)
;DROP TRHOUGH TO CONSTRUCT THE BDL
;FROM ABOVE. HERE TO CONSTRUCT THE BDL AND QUEUE THE BUFFER-IN TRANSACTION
MOVEI T1,3(P4) ;GET START OF DATA MESSAGE AREA
SUBI T1,(F) ;CONVERT TO OFFSET ON PAGE
LSH T1,2 ;CONVERT FROM WORDS TO BYTES
ADDI T1,2 ;STARTS AT 3+1/2 WDS SO USER DATA IS WORD
;ALIGNED & CAN UBABLT
ADD T1,KDLADR(F) ;CONVERT PAGE OFFSET TO UNIBUS ADDR
MOVEI T2,-16(T1) ;SAVE ADDR OF BDL FOR LATER
;BDL1.1
HRLZM T1,0(P4) ;STORE LOW 16 BITS OF ADDR IN THE BDL
;BDL1.3
LSH T1,-6 ;SHIFT BITS 16 & 17 DOWN TO SCREWY POSITION
ANDI T1,BDLXAD ;MASK TO GET JUST THE EXTENDED ADDR BITS
TRO T1,BDLLDS ;SAY THAT THIS IS THE LAST BD
HRLZM T1,1(P4) ;STORE THIRD BDL WORD
;BDL1.2
MOVEI T1,KDLMMS*4 ;GET MAX MESSAGE SIZE
HRRM T1,0(P4) ;AND STORE THAT IN THE LENGTH FIELD
MOVSI T1,(1B0) ;GET THE BDL IN USE BIT
IORM T1,0(P4) ; AND MARK BDL AS IN USE
;NOW QUEUE THE BUFFER IN TRANSACTION
MOVE W,KDLKDP(F) ;JUST MAKE SURE "W" IS SET UP
MOVE T1,KDLINE(F) ;GET OUR LINE NUMBER
LSH T1,10 ;SHIFT INTO THE HIGH BYTE
TRO T1,KMCIOT ;SAY THAT WE ARE QUEUEING AN INPUT BUFFER
HRLZ T3,T2 ;COPY THE BDL ADDRESS
LSH T2,-2 ;SHIFT TWO HIGH ORDER BITS DOWN
IOR T2,T3 ;COMBINE SEL4 AND SEL6
AND T2,[XWD 177777,140000] ;MASK OUT TRASH
PUSHJ P,KMCINP ;QUEUE TRANSACTION
POPJ P, ;AND RETURN
POPJ P, ;(IGNORE SKIP RETURN)
SUBTTL XMTBUF -- ROUTINE TO TRANSMIT DDCMP CONTROL AND DATA MESSAGES.
;Called from "most anywhere" with interrupts off and:
; F, W := KDL and KDP pointers
;
XMTBUF: PUSHJ P,SAVE4## ;THIS CODE CLOBBERS THEM ALL
MOVE T1,KDLSTS(F) ;GET THE WORD WITH THE MESSAGE FLAGS
AND T1,[XMTBTS] ; AND GET JUST THE FLAGS
LDB T2,KDLSTA## ;GET THE STATE
CAIG T2,KD%FLS ; AND IF WE'RE FLUSHING,
POPJ P, ; THEN DON'T DO ANYTHING
CAIN T2,KD%MAI ;IF WE ARE IN MAINT STATE, THEN
SETZ T1, ; WE CAN ONLY SEND DATA (MAINT) MSGS
SKIPE KDLWTO(F) ;IF THERE IS DATA TO GO,
TLO T1,(KDSDAT) ; THEN SET THE BIT SAYING SO.
JFFO T1,XMTBF1 ;JUMP IF THERE ARE ANY MSGS TO SEND
POPJ P, ; IF NO MSGS TO SEND, EXIT
XMTBF1: AOSG T1,KDLXBC(F) ;INCREMENT COUNT OF XMIT BUFFERS QUEUED
PUSHJ P,NTDSTP## ;++ SHOULD NEVER BE NEGATIVE
CAILE T1,2 ;IF LESS THAN 2 BUFFERS QUEUED, WE CAN Q MORE
JRST [SOS KDLXBC(F) ;CORRECT THE COUNT
POPJ P,] ; AND RETURN
SOS T1 ;T1 NOW IS EITHER 0 ! 1
MOVE T3,KDLSTS(F) ;GET THE STATUS (CONTAINS NEXT BUFFER OUT BIT)
TRNE T3,KDSNXB ;IF NEXT BUFFER OUT ISN'T 0
XORI T1,1 ;THEN NEXT BUFFER IN IS DIFFERENT
MOVEI P4,@[CAI KDLXD1(F) ;LOAD PROPER BDL ADDRESS
CAI KDLXD2(F)](T1) ;FROM T1
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 (WE SET IT LATER)
;NOW P4 := BDL ADDRESS, TIME TO DISPATCH TO PROPER ROUTINE.
PUSHJ P,@[ ;DISPATCH ON JFFO CODE
JRST XMTSTR ;SEND START
JRST XMTSTK ;SEND STACK
JRST XMTNAK ;SEND NAK
JRST XMTREP ;SEND REP
JRST XMTDAT ;SEND DATA
JRST XMTACK ;SEND ACK
](T2) ;MUST REMEMBER NOT TO CLOBBER "T2" ABOVE...
JFCL ;IGNORE IT IF KMC-11 CRASHES
POPJ P, ;BACK TO CALLER
XMTSTR: MOVEI T1,%START ;GET THE START MESSAGE TYPE
MOVSI T2,(KDSSTR) ;GET THE "BIT TO CLEAR"
DPB T2,KDLTIM## ;START THE TIMER
PUSHJ P,XMTSET ;SET UP BDL, T1, T2, T4 ETC...
PJRST KMCINP ; QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
XMTSTK: MOVEI T1,%STACK ;GET THE STACK MESSAGE TYPE
MOVSI T2,(KDSSTK) ;GET THE "BIT TO CLEAR"
DPB T2,KDLTIM## ;START THE TIMER
PUSHJ P,XMTSET ;SET UP BDL, T1, T2, T4 ETC...
PJRST KMCINP ; QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
XMTREP: MOVEI T1,%REP ;GET THE REP MESSAGE TYPE
MOVSI T2,(KDSREP) ;GET THE "BIT TO CLEAR"
DPB T2,KDLTIM## ;START THE TIMER
PUSHJ P,XMTSET ;SET UP BDL, T1, T2, T4 ETC...
LDB T3,KDLLMX## ;GET LAST TRANSMIT MSG. NUMBER ASSIGNED
DPB T3,BYTABL+4 ; AND SEND THAT AS THE MSG NUMBER
PJRST KMCINP ; QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
XMTNAK: MOVSI T1,(KDSACK) ;IF ACK QUEUE'D TOO
TDNE T1,KDLSTS(F) ;I'M VERY CONFUSED
PUSHJ P,NTDSTP## ;SO I'D BETTER QUIT
MOVEI T1,%NAK ;GET THE NAK MESSAGE TYPE
MOVSI T2,(KDSNAK) ;GET THE "BIT TO CLEAR"
PUSHJ P,XMTSET ;SET UP BDL, T1, T2, T4 ETC...
PUSH P,T1 ;SAVE T1
PUSH P,T2 ; AND T2 (NAKTRN GET'S EM)
LDB T1,KDLXNK## ;GET THE NAK CODE.
IORI T1,QSYNC!SELECT ;PUT ON THE TWO "REQUIRED" BITS
DPB T1,BYTABL+2 ; AND STORE THAT IN THE MESSAGE
ANDI T1,77 ;GET JUST THE NAK CODE BACK AGAIN,
PUSHJ P,NAKTRN ; CONVERT IT INTO A TABLE INDEX
ADDI T1,KDLNKX(F) ; RELOCATE TO OUR XMIT NAK COUNTER TABLE
AOS (T1) ; AND INCREMENT THE ENTRY.
POP P,T2 ;GET T2 AND
POP P,T1 ; BACK
LDB T3,KDLRMN## ;GET LAST MSG. RECEIVED
DPB T3,BYTABL+3 ; SEND THAT AS IMPLICIT ACK
PJRST KMCINP ;QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
XMTACK: MOVEI T1,%ACK ;GET THE ACK MESSAGE TYPE
MOVSI T2,(KDSACK) ;GET THE "BIT TO CLEAR"
PUSHJ P,XMTSET ;SET UP BDL, T1, T2, T4 ETC...
LDB T3,KDLRMN## ;GET LAST MSG. RECEIVED
DPB T3,BYTABL+3 ; SEND THAT AS IMPLICIT ACK
PJRST KMCINP ;QUEUE TRANSACTION, CLEAR THE BIT AND RETURN
; XMTSET is a utility subroutine used in constructing the various
;DDCMP control messages. It performs the functions of:
; 1) Clearing the message request bit in KDLSTS.
; 2) Constructing a BDL for the control message
; 3) Filling in selected portions of the control message.
; Specifically, the header (ENQ), type, control bits and
; station address (= 1).
; 4) Sets things up to make it convenient to call KMCINP
;
;Called from XMT??? with:
; F, W := KDL and KDP pointers
; T1 := Control message type
; T2 := Message request bit in KDLSTS to clear.
; P4 := Address of the BDL to use for the message
;
;Returns "CPOPJ" with:
; T1, T2 := Set up with SEL2, SEL4 and SEL6 for KMCINP.
; T4 := A CBP to the first byte of the message (The ENQ)
;
XMTSET: PUSH P,T1 ;SAVE MESSAGE TYPE FOR A BIT
ADDI T1,KDLCTX-1(F) ;MAKE MESSAGE TYPE A POINTER INTO THE XMITTED
AOS (T1) ; COUNTS TABLE, AND COUNT THIS CONTROL MSG
ANDCAM T2,KDLSTS(F) ;CLEAR THE REQUEST BIT.
MOVEI T4,4(P4) ;GET ADDRESS OF THE DATA AREA
MOVEI T2,(T4) ;GET ADDRESS OF DATA
SUBI T2,(F) ;CONVERT TO OFFSET WITHIN PAGE
LSH T2,2 ;GET PAGE OFFSET IN BYTES
ADD T2,KDLADR(F) ;MAKE IT A UNIBUS ADDRESS
;BDL1.1
MOVEI T1,400000(T2) ;MAKE SURE WE SET THE IN-USE MARK BIT
HRLZM T1,0(P4) ;STORE -11 ADDRESS OF DDCMP HEADER
;BDL1.2
MOVEI T1,6 ;GET THE LENGTH OF THE DDCMP HEADER
HRRM T1,0(P4) ;WRITE THE LENGTH
;BDL1.3
MOVEI T1,(T2) ;GET ADDRESS OF THE HEADER AGAIN
LSH T1,-6 ;SHIFT TO GET TWO HIGH BITS OF ADDRESS
ANDI T1,BDLXAD ;MASK TO JUST 2 BITS (6000)
TRO T1,BDLLDS!BDLRSY!BDLSOM!BDLEOM ;RESYNC, START, END OF MESSAGE
HRLZM T1,1(P4) ;WRITE THIRD WORD OF HEADER
;FALL THROUGH TO WRITE THE CONTROL MESSAGE
;FROM ABOVE. HERE TO WRITE THE CONTROL MESSAGE
MOVE W,KDLKDP(F) ;MAKE SURE "W" IS SET UP
MOVE T1,KDLINE(F) ;GET THE LINE #
LSH T1,10 ;PUT LINE NUMBER IN THE HIGH BYTE
ADDI T2,-20 ;CORRECT TO GET ADDRESS OF THE BDL
HRLZ T3,T2 ;COPY THE BDL'S -11 ADDRESS
LSH T2,-2 ;SHIFT HIGH TWO BITS DOWN
IOR T2,T3 ;COMBINE
AND T2,[XWD 177777,140000] ;MASK OUT TRASH
MOVEI T3,ENQ ;GET THE CONTROL MESSAGE HEADER
DPB T3,BYTABL+0 ; AND SET THAT
POP P,T3 ;GET THE CONTROL MESSAGE TYPE
DPB T3,BYTABL+1 ; AND SET THAT
MOVEI T3,QSYNC!SELECT ;THESE TWO BITS ARE REQUIRED BY DDCMP
DPB T3,BYTABL+2 ; SO PUT THEM IN THE MESSAGE
SETZ T3, ;ZERO THE MESSAGE NUMBER FIELDS
DPB T3,BYTABL+3 ; ACK FIELD
DPB T3,BYTABL+4 ; NUMBER FIELD
MOVEI T3,1 ;GET THE POINT-TO-POINT STATION ADDR = 1
DPB T3,BYTABL+5 ;SET STATION ADDRESS
POPJ P, ;AND THAT'S ALL
SUBTTL XMTDAT -- ROUTINE TO SEND A DATA MESSAGE.
; XMTDAT constructs DDCMP data messages. It:
; 1) Requeues the BFR of the message it is outputing
; to the "awaiting ACK" (KDLWTA) queue. (We are
; interlocked, so there is no fear of races.)
; 2) Copies the "header" portion of the message to the
; appropriate output buffer in the KDL page.
; 3) Copies, with the appropriate compression, the "data"
; portion of the BFR. (Ie. The PCBPT2 data.)
; 4) It the fills in the DDCMP header, constructs a BDL
; consisting of two buffer descriptors. (One for the
; DDCMP header, and one for the data portion)
; 5) It queues BUFFER-OUT(OUTPUT) transaction for the KMC-11
;
;Called from XMTBUF with:
; F, W := KDL and KDP pointers
; P4 := The address of the BDL to use for the message.
;Returns
; CPOPJ If KMC died.
; CPOPJ1 With message queued if all went OK.
;
;Register usage.
; S = The character we are currently hacking
; T1 = Count of chars left in BFR
; T2 = Byte pointer into the BFR
; 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 KDLXD?. Ie. the approiate buffer descriptor.
;
; U = Pointer to the BFR
; F = Pointer to kdl line block table
; W = Pointer to kdp table
;
;FIRST MAKE SURE THAT WE DON'T HAVE TOO MANY MSGS IN THE "PIPE"
XMTDAT: AOS T1,KDLCTA(F) ;COUNT UP THE MESSAGES AWAITING ACK
CAIL T1,MAXOUT ; BUT IF THERE ARE TOO MANY,
JRST [SOS KDLCTA(F) ; CORRECT THE ACK AND
SOS KDLXBC(F) ; NUMBER OF BUFFERS QUEUED COUNTS
SETZM (P4) ; MARK THE BDL AS "FREE"
POPJ P,] ; AND DON'T DO ANYTHING.
;NOW REQUEUE THE BFR FROM THE WTO QUEUE TO THE WTA.
PUSH P,U ;SAVE "U" FOR THE DURATION
HRRZ U,KDLWTO(F) ;GET THE FIRST ENTRY ON THE WTO QUEUE
HRRZ T1,MB.NXT(U) ;GET THE SECOND ENTRY ...
HRRZM T1,KDLWTO(F) ;NOW MAKE THE SECOND THE NEW FIRST
HLLZS MB.NXT(U) ;MAKE SURE THERE ARE NO STRAY POINTERS
HRRZ T1,KDLWTA(F) ;GET ADDR OF FIRST ON WTA QUEUE
JUMPE T1,[HRRZM U,KDLWTA(F) ;IF NONE, MAKE THIS THE FIRST
JRST XMTDA2] ; AND GO PROCESS IT
XMTDA1: HRRZ T2,MB.NXT(T1) ;SEE IF WE'VE REACHED THE END OF THE WTQ Q
JUMPE T2,[HRRM U,MB.NXT(T1) ;IF SO, APPEND THIS ONE TO THE END
JRST XMTDA2] ; AND TO PROCESS IT
MOVE T1,T2 ;IF NOT AT END OF QUEUE, GO TO NEXT BFR
JRST XMTDA1 ; AND SEE IF IT IS THE END OF THE QUEUE
;NOW SEE IF THE MESSAGE HAS BEEN ASSIGNED A NUMBER. IF NOT, THEN
; ASSIGN A NEW ONE
XMTDA2: LDB T1,KDLSTA## ;GET OUR STATE, AND
CAIN T1,KD%MAI ; IF WE ARE IN MAINT STATE
JRST XMTDA3 ; THEN DON'T ASSIGN A MESSAGE NUMBER
MOVE T1,MB.MSN(U) ;(MB.MSNN) = -1 MEANS NO NUMBER ASSIGNED
JUMPGE T1,XMTDA3 ;IF RESENDING MSG, DON'T ASSIGN A NUMBER
LDB T1,KDLLMX## ;GET LAST NUMBER ASSIGNED
ADDI T1,1 ;INCREMENT IT TO THE NEXT
ANDI T1,^O377 ;MASK TO 8 BITS
DPB T1,KDLLMX## ;REMEMBER LAST MESSAGE ASSIGNED
MOVEM T1,MB.MSN(U) ; AND ASSIGN IT TO THIS MESSAGE
; FROM HERE ON, THE FOLLOWING CONVENTIONS GENERALLY HOLDS
; 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.
XMTDA3: MOVE T2,MB.FMS(U) ;NOW SEE IF THIS IS ANF-10 OR DECNET FORMAT
JUMPE T2,XMTANF ; IF IT'S ANF, GO HANDLE FUNNY BUFFERS
MOVSI T4,(POINT 8) ;START OFF WITH T4 BEING A BYTE POINTER
HRRI T4,6(P4) ; TO THE OUTPUT BUFFER
PUSH P,[EXP 0] ;INITIALIZE THE "TOTAL" COUNT
XMTDEC: ;ADD CHECK FOR BEING OVERLOADED
MOVE T6,MD.ALA(T2) ;BYTE POINTER IS INDEXED BY T6
MOVE T1,MD.AUX(T2) ; AND IT'S BYTE ADDRESS
MOVE T3,MD.BYT(T2) ;COPY THE LENGTH FOR THE DESTINATION
ADDM T3,(P) ; AND ACCUMULATE THE TOTAL COPIED
XMTDE0: SOJL T3,XMTDE1 ;COUNT DOWN NUMBER OF BYTES
ILDB T5,T1 ;GET A BYTE
IDPB T5,T4 ;PUT A BYTE
JRST XMTDE0 ;AND LOOP
XMTDE1: 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
CAILE T3,KDLMMS*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,KDLSTA## ;FIRST GET THE STATE, AND
CAIN T1,KD%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)
HRLZ T2,PCBPTR(U) ;GET THE ADDRESS TO START BLT'ING FROM
HRRI T2,6(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) ;GET A SKELETON BYTE POINTER AND
HRRI T1,6(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, DON'T TRY TO SEND IT
PUSHJ P,@[EXP C.NC,C.LP,C.BN](T1) ;CALL CONVERSION ROUTINE
JRST XMTXIT ;GO SWAP THE BYTES AND SEND IT
;XMTANM ROUTINE TO SEND AN ANF-10 MAINT MESSAGE (STC BLOCK)
XMTANM: HLRZ T3,STCBLK(U) ;GET THE BYTE COUNT WHERE XMTXIT WANTS IT
CAIL T3,KDLMMS*4 ;MAKE SURE THAT THE LENGTH IS REASONABLE
PUSHJ P,NTDSTP## ;IF NOT, TELL SOMEONE
MOVEI T1,6(P4) ;GET THE ADDRESS OF THE KDL BUFFER
HRLI T1,STCDAT(U) ;GET THE ADDRESS OF THE MAINT DATA
MOVE T2,T3 ;GET THE LENGTH (IN BYTES)
SOS T2 ;ROUND UP(+3), LESS 1 WD (-4)
LSH T2,-2 ;CONVERT TO WORDS
ADDI T2,(T1) ;CALCULATE THE END OF THE "BLT"
BLTBU T1,(T2) ;COPY AND SWAP IN ONE SWELL FOOP
JRST XMTDON ;DONE
;NOW WE HAVE THE BUFFER FILLED, BUT WE MUST SWAP THE BYTES FOR THE
; STUPID UBA...
; T3 := THE NUMBER OF BYTES IN THE BUFFER
;
;DECNET MESSAGES CAN COME AS CHAINED MSDS OF ODD BYTE LENGTHS, AND
;ANF MESSAGES CAN HAVE CONVERSION. SO UBABLT HAS TO WAIT TILL NOW,
;WHEN THE DATA IS IN A FINAL FORM, AND THE START IS WORD-ALIGNED.
XMTXIT: MOVEI T1,3(T3) ;GET THE NUMBER OF BYTES, ROUND UP
ASH T1,-2 ;CONVERT TO A -10 WORD COUNT
ADDI T1,6(P4) ;SKIP THE BUFFER DESCRIPTIOR, PT TO USER DATA
MOVEI T4,6(P4) ;T1 HAS THE END ADDR +1, T4 THE START
HRLI T4,0(T4) ;THIS BLT GOES IN PLACE (SET SOURCE)
BLTBU T4,-1(T1) ;DO THE SWAP
;HERE TO WRITE THE BUFFER DESCRIPTOR HEADER
XMTDON: LDB T1,KDLSTA## ;GET STATE (MAINT OR NOT) WE USE IT A LOT
CAIN T1,KD%MAI ;IF THIS IS A MAINT MSG, THEN
AOSA KDLMAX(F) ; THEN COUNT IT.
AOS KDLDTX(F) ;OTHERWISE IT'S DATA
MOVEI T4,3(P4) ;BUILD A PRE-DECREMENTED CBP TO THE
HRLI T4,3 ; HEADER AREA
;HEADER
MOVEI S,SOH ;ASSUME THAT THIS IS A DATA MESSAGE
CAIN T1,KD%MAI ; BUT IF IT TURNS OUT TO BE MAINT
MOVEI S,DLE ; THEN CHANGE THE HEADER CHAR
PUSHJ P,IDPCB ;WRITE THE HEADER CHARACTER
;CC1
MOVE S,T3 ;GET THE LOW 8 BITS OF THE COUNT
ANDI S,377 ; AND JUST THE LOW 8 BITS
PUSHJ P,IDPCB ;WRITE THE FIRST PART OF THE COUNT
;CC2
MOVE S,T3 ;GET THE COUNT AGAIN
LSH S,-10 ; AND THIS TIME JUST THE TOP 6 BITS
TRZE S,777700 ;MAKE SURE THE COUNT WAS REASONABLE
PUSHJ P,NTDSTP## ; AND STOP IF BUFFER WAS TO BIG
M9312:: TRO S,SELECT ;SET THE SELECT (POINT 2 POINT)
CAIN T1,KD%MAI ;AND IF WE ARE IN MAINT MODE,
TRO S,QSYNC ;SET THE QSYNC ALSO (MAKE THE DMR HAPPY)
PUSHJ P,IDPCB ;STORE THE UPPER-COUNT AND FLAGS
;RESP
SETZ S, ;ASSUME THIS IS MAINT, HENCE ZERO REPS
CAIE T1,KD%MAI ; BUT IF WE ARE SENDING REAL DATA,
LDB S,KDLRMN## ;LOAD THE "IMPLIED ACK" NUMBER
PUSHJ P,IDPCB ;WRITE THE ACK FIELD
MOVSI S,(KDSACK) ;NO NEED TO ACK
CAIE T1,KD%MAI ;AT LEAST IF A DATA MESSAGE
ANDCAM S,KDLSTS(F) ;BECAUSE THE RESP IS JUST AS GOOD
;NOTE: RH OF S IS STILL ZERO
;NUM
CAIE T1,KD%MAI ;IF WE ARE SENDING DATA MESSAGES
MOVE S,MB.MSN(U) ;GET THE MESSAGE'S NUMBER
PUSHJ P,IDPCB ;WRITE THE MESSAGE NUMBER FIELD
;ADDR
MOVEI S,1 ;GET OUR STATION ADDRESS (1 = P 2 P)
PUSHJ P,IDPCB ; AND SEND IT
;TIMER
SETZ S, ;PREPARE TO START TIMER
LDB T1,KDLTIM## ;IF IT'S RUNNING
CAIN T1,KD%MXT ;WE DON'T CHANGE IT
DPB S,KDLTIM## ;IT'S NOT, START IT
;IT'S OK NOT TO CHECK MAINT - TIMER IS IGNORED
;BCC1
;BCC2
;FROM ABOVE. AT THIS POINT, THE ENTIRE MESSAGE IS COMPLETE.
; ALL THAT IS LEFT IS TO BUILD THE BUFFER DESCRIPTOR LIST AND
; GIVE IT TO THE KMC. P4 := ADDRESS OF THE BDL
MOVEI T4,(P4) ;GET ADDRESS OF THE BDL AREA
TLO T4,(POINT 18,0) ;MAKE A HALF WORD BYTE POINTER
MOVEI T2,(P4) ;GET ADDRESS OF THE DATA
SUBI T2,(F) ;GET JUST THE OFFSET WITHIN THE PAGE
LSH T2,2 ;GET PAGE OFFSET IN BYTES
ADD T2,KDLADR(F) ;MAKE IT A UNIBUS ADDRESS
MOVEI S,20(T2) ;ADDR OF DDCMP HEADER IS 20 BYTES PAST BDL
;BDL1.1
IORI S,(1B0) ;MAKE SURE WE SET THE IN-USE MARK BIT
IDPB S,T4 ;STORE -11 ADDRESS OF DDCMP HEADER
;BDL1.2
MOVEI S,6 ;GET THE LENGTH OF THE DDCMP HEADER
IDPB S,T4 ;WRITE THE LENGTH
;BDL1.3
MOVEI S,20(T2) ;GET ADDRESS OF THE HEADER AGAIN
LSH S,-6 ;SHIFT TO GET TWO HIGH BITS OF ADDRESS
ANDI S,BDLXAD ;MASK TO JUST 2 BITS (6000)
TRO S,BDLRSY!BDLSOM!BDLEOM ;RESYNC, START AND END OF MESSAGE
IDPB S,T4 ;WRITE THIRD WORD
;BDL2.1
MOVEI S,30(T2) ;GET ADDRESS AGAIN (OFFSET PAST DDCMP HEADER)
IDPB S,T4 ;STORE LOW 16 BITS OF ADDR
;BDL2.2
IDPB T3,T4 ;T3 STILL HAS THE LENGTH (REMEMBER...)
;BDL2.3
MOVEI S,30(T2) ;GET ADDR FOR NEARLY THE LAST TIME
LSH S,-6 ;SHIFT TO GET HIGH ORDER 2 BITS
ANDI S,BDLXAD ;MASK TO 2 BITS (6000)
TRO S,BDLLDS!BDLSOM!BDLEOM ;LAST BDL, START AND END OF MESSAGE
IDPB S,T4 ;STORE LAST DESCRIPTOR WORD
;BDL IS NOW COMPLETE. TIME TO QUEUE IT TO KMCINP
MOVE W,KDLKDP(F) ;GET THE ADDRESS OF THE KDP BLOCK
MOVE T1,KDLINE(F) ;GET THE LINE #
LSH T1,10 ;PUT LINE NUMBER IN THE HIGH BYTE
HRLZ T3,T2 ;COPY THE BDL'S -11 ADDRESS
LSH T2,-2 ;SHIFT HIGH TWO BITS DOWN
IOR T2,T3 ;COMBINE
AND T2,[XWD 177777,140000] ;MASK OUT TRASH
POP P,U ;RESTORE THE REG USED TO HOLD BFR ADDR
PJRST KMCINP ;WILL SKIP IF KMC STILL RUNNING
SUBTTL XMTJNK - ANF-10 SPECIFIC CONVERSION/COMPRESSION ROUTINES
;C.NC COPY. NO CONVERSION
C.NC:
MOVE T1,PCBPT2(U) ;GET THE BYTE POINTER FOR IT
MOVE T2,PCBCT2(U) ;GET THE LENGTH OF THE STRING TO MOVE
ADD T3,T2 ;UPDATE T3 TO REFLECT IMPENDING COPY
C.NC0: SOJL T2,CPOPJ## ;COUNT DOWN BYTES TO COPY
ILDB T5,T1 ;GET A BYTE
IDPB T5,T4 ;PUT A BYTE
JRST C.NC0 ;AND LOOP
;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 THE EXTEND 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 CHKACK -- PROCESS THE "ACK" FIELD OF CONTROL AND DATA MSGS.
; CHKACK is a routine that scans the "awaiting ack" message queue for
;messages whose numbers are less than, or equal to the "ack" field of the
;message header pointed to by "T4". Such messages are de-queued and returned
;to the driver via DC.IOD
;
;Called from RCV??? with:
; F, W := KDL and KDP pointers
; T4 := a CBP to the first byte of the message.
; (Set up by XMTSET)
;Returns
; CPOPJ ALWAYS
;
CHKACK: LDB T1,KDLSTA## ;GET THE DDCMP "STATE" VARIABLE
CAIN T1,KD%STK ;SEE IF WE ARE SENDING STACKS
JRST [MOVSI S,(KDSSTR!KDSSTK) ;IF WE ARE, CLEAR SEND STACK!START
ANDCAB S,KDLSTS(F) ; SO WE WON'T CLOBBER NEWLY STARTED LINE
MOVEI T1,KD%RUN ;GET THE "RUN" STATE VALUE
DPB T1,KDLSTA##;AND SET THE STATE TO "RUNNING"
PUSHJ P,KDLPUP ;TELL NETSER THE LINE'S UP
JRST CHKAC1] ;RETURN TO MAIN LINE CODE
CAIE T1,KD%RUN ;ARE WE "RUNNING"
POPJ P, ;IF NOT RUNNING, GIVE ERROR RETURN
;THE LINE IS UP. FALL THROUGH TO PROCESS THE ACK
;FROM ABOVE
;NOW LOOP INCREMENTING "LAST MESSAGE ACKED" UNTIL IT EITHER
; EQUALS "LAST MESSAGE SENT" OR THIS ACK'S VALUE.
; MESSAGES THAT GET ACK'ED ARE GIVEN BACK TO NETSER
CHKAC1: LDB T1,BYTABL+5 ;GET THE ACK FIELD OF THE MESSAGE
LDB T2,KDLLMA## ;GET THE LAST MESSAGE ACKED
CAIN T1,(T2) ;SEE IF THIS IS A "NEW" ACK
JRST CHKAC4 ;IF WE'VE SEEN THIS ACK, CLEAR REP CNT AND EXIT
SUBI T1,(T2) ;GET THE DIFFERENCE BETWEEN MESSAGES
SKIPG T1 ;CHECK FOR MESSAGE NUMBER WRAP-AROUND
ADDI T1,400 ;IF WRAP-AROUND, CORRECT TO POSITIVE VALUE
CAIL T1,MAXOUT ;SEE IF THIS ACK IS IN RANGE
POPJ P, ;IF ACK IS OUT OF RANGE, RETURN WITH-OUT
; PROCESSING. (MIGHT WANT TO GIVE ERROR...)
LDB T1,KDLLMA## ;GET THE "LAST MESSAGE ACKED"
ADDI T1,1 ;INCREMENT IT TO NEXT MESSAGE
ANDI T1,377 ;MASK TO 8 BITS
DPB T1,KDLLMA## ;STORE THE NEW "LAST MESSAGE ACKED"
HRRZ T3,KDLWTA(F) ;GET ADDRESS OF NEXT MESSAGE AWAITING AN ACK
JUMPE T3,CHKAC2 ;NOTHING ON THE WAIT ACK QUEUE, TRY OUTPUT
HRRZ T2,MB.NXT(T3) ;GET THE ADDRESS OF THE MESSAGE AFTER THAT
HRRZM T2,KDLWTA(F) ;MAKE THAT MESSAGE THE "NEXT" MESSAGE WAITING
SOSGE KDLCTA(F) ;SAY THAT THERE IS ONE LESS AWAITING ACK
PUSHJ P,NTDSTP## ;WE'VE SCREWED UP THE ACK COUNT?
PJRST CHKAC3 ;GO RETURN THE MESSAGE TO NETSER
;HERE IF BFR IS NOT ON THE WAIT ACK QUEUE. CHECK FOR IT ON OUTPUT WAIT
CHKAC2: HRRZ T3,KDLWTO(F) ;GET THE FIRST ENTRY FROM THE OUTPUT WAIT QUEUE
SKIPN T3 ;IF THERE ISN'T ONE, THEN WE GOT AN ACK FOR
PJSP T1,KDLERR ; A MSG WE NEVER SENT. THIS IS A FATAL ERROR
HRRZ T2,MB.NXT(T3) ;GET THE NEXT MSG OFF THE QUEUE
HRRZM T2,KDLWTO(F) ; AND MAKE IT THE FIRST AWAITING OUTPUT
;DROP THROUGH TO RETURN THE BFR
;HERE WE HAVE T3 := THE BFR THAT HAS BEEN ACKED. FIRST MAKE SURE
; THAT IT'S REASONABLE, THEN RETURN IT TO THE DRIVER.
CHKAC3: MOVE T2,MB.MSN(T3) ;GET THE DDCMP MESSAGE NUMBER
SKIPLE T3 ;MAKE SURE WE GOT A BFR
CAIE T1,(T2) ; AND IT BETTER BE THE "NEXT" ONE
PUSHJ P,NTDSTP## ;++ MESSAGES OUT OF ORDER. KDPINT SCREWED UP.
HLLZS MB.NXT(T3) ;NO STRAY POINTERS PLEASE
PUSH P,T4 ;PRESERVE THE POINTER TO THE MSG
MOVEI T1,DC.IOD ;GET THE "OUTPUT DONE" FUNCTION CODE
PUSHJ P,CALDVR ; AND TELL THE DRIVER THE GOOD NEWS
POP P,T4 ;RECOVER THE POINTER TO THE MESSAGE
SETZ T1, ;GET A ZERO AND USE IT TO CLEAR THE REP
DPB T1,KDLRPC## ; COUNTER SINCE THINGS SEEM TO BE WORKING.
DPB T1,KDLTIM## ;RESTART REP TIMER
JRST CHKAC1 ;LOOP INCREMENTING "LAST MESSAGE ACK'ED" UNTIL
; IT EQUALS THE "ACK" FIELD OF THE MESSAGE.
CHKAC4: LDB T1,KDLLMX## ;GET THE LAST MESSAGE SENT
; LDB T2,KDLLMA## ;(FROM ABOVE)
CAIE T1,(T2) ;SEE IF ANY MESSAGES ARE OUTSTANDING
POPJ P, ;IF SO, LEAVE REP COUNTER RUNNING
SETZ T1, ;GET A ZERO AND USE IT TO CLEAR THE REP
DPB T1,KDLRPC## ; COUNTER SINCE NO MESSAGES OUTSTANDING
PUSHJ P,STPTMR ;STOP THE TIMER, THERE'S NOTHING OUTSTANDING
POPJ P,
SUBTTL GETBDL -- ROUTINE TO FIND A BDL GIVEN IT'S UNIBUS ADDRESS.
;Called from KDP?O? with:
; F, W := KDL and KDP pointers
; P1, P2, P3 := SEL2, SEL4 and SEL6 of the KMC-11 (Which contain
; the unibus address of a BDL)
;Returns
; CPOPJ With T4 := CBP pointing to the BDL described in P2 & P3.
;
GETBDL: MOVE T1,P2 ;GET BUFFER DESCRIPTOR LIST ADDRESS (11 ADDR)
MOVE T4,P3 ;GET 2 HIGH ORDER BITS
LSH T4,2 ;POSITION THEM AS HIGH BITS IN 18 BIT ADDR
ANDI T1,177777 ;GET LOW ORDER 16 BITS
ANDI T4,600000 ;GET HIGH ORDER 2 BITS
IORI T4,(T1) ;"OR" THEM TO GET ENTIRE 18 BIT 11 ADDRESS
SUB T4,KDLADR(F) ;T1 NOW HAS BYTE OFFSET FROM START OF PAGE
DPB T4,[POINT 2,T4,15] ;MOVE THE TWO LOW ORDER BITS TO HIGH HALF
LSH T4,-2 ;SHIFT WHOLE MESS INTO A CANONICAL BP
TDNE T4,[XWD 777774,^-<KDLLEN-1>] ;MAKE SURE IT MAPS TO A PAGE
PUSHJ P,NTDSTP## ;++ OUR MAP'S ARE SCREWED UP...
ADDI T4,(F) ;RELOCATE TO A PAGE ADDRESS
POPJ P, ;ALL DONE. T4 := A CBP TO THE BDL
SUBTTL KMCINP -- ROUTINE TO QUEUE TRANSACTIONS FOR THE KMC-11
;Called with:
; W := KDP pointer
; T1 := XWD 0,SEL2
; T2 := XWD SEL4,SEL6
;Returns
; CPOPJ KMC-11 not running
; CPOPJ1 Transaction described by T1 & T2 has been queued.
; RQI has been set to request an interrupt on vector "A".
; KDPAIV will then process the top transaction on the queue.
;
KMCINU: KDPOFF ;ENTRY TO TURN INTERRUPTS OFF FIRST
PUSHJ P,KMCINP ;QUEUE THE TRANSACTION
SKIPA ;HANDLE SKIP RETURNS PROPERLY
AOS (P) ;GIVE SKIP
KDPONJ: KDPON ;RE-ENABLE INTERRUPTS
POPJ P, ;ALL DONE
KMCINP: MOVE T3,KDPIQP(W) ;GET INDEX OF NEXT ENTRY IN THE QUEUE
LSH T3,1 ;MAKE IT AN OFFSET (ENTRYS ARE 2 WDS)
ADDI T3,KDPINQ(W) ;RELOCATE TO THE ADDRESS OF THE QUEUE
MOVEM T1,0(T3) ;STORE SEL2
MOVEM T2,1(T3) ;STORE XWD SEL4,SEL6
AOS T3,KDPIQP(W) ;ADVANCE THE "PUTTER"'S INDEX
CAIL T3,KDPQLN ;IF WE NEED TO WRAP AROUND, THEN
SETZB T3,KDPIQP(W) ; THEN WRAP TO THE FIRST ENTRY
CAMN T3,KDPIQT(W) ;IS THE QUEUE FULL (PUTTER = TAKER)
PJSP T1,KDPERR ; IF SO, KMC MUST BE DEAD. CRASH IT.
MOVEI T3,KMCRQI ;GET RQI AND SET IT IN BSEL0
BSIO T3,@KDPCSR(W) ; THIS WILL CAUSE A VECTOR "A" INTERRUPT
RETSKP ;GOOD RETURN
;NAKTRN - ROUTINE TO "TRANSLATE" NAKS. SINCE NAK CODES ARE SPARSE, AND
; WE WISH TO USE DENSE NAK COUNT TABLES, THIS ROUTINE MAPS FROM
; NAK CODE TO TABLE INDEX
;
;CALL MOVEI T1,NAK-CODE
; PUSHJ P,NAKTRN
;RETURN CPOPJ ;ALWAYS, T1 := TRANSLATED NAK,
; ; OR 0 IF UN-RECOGNIZED NAK (RANDOM NAK)
;
NAKTRN: PUSHJ P,SAVE1## ;WE NEED A TEMP. SO WE USE P1
MOVEI T2,(T1) ;COPY THE NAK CODE
MOVEI T1,1 ;GET FIRST GUESS AT TABLE INDEX
MOVE P1,[POINT 6,NAKTBL] ;GET A POINTER TO THE NAK XLATION TBL
NAKTR1: ILDB T3,P1 ;GET THE NEXT NAK CODE
CAIN T3,(T2) ;DOES IT MATCH OUR NAK
POPJ P, ;IF IT MATCHES, RETURN. T1 HAS TABLE INDEX
SKIPE T3 ;WAS THIS A ZERO BYTE?
AOJA T1,NAKTR1 ;IF NOT AT END OF TABLE, KEEP LOOPING
SETZ T1, ;TABLE EXHAUSTED, RETURN NAK TYPE OF ZERO
POPJ P, ;ALL DONE
NAKTBL: BYTE (6)1,2,3,^D8,^D9,^D16,^D17,0 ;TABLE OF NAKS
;CTOTLY - ROUTINE TO "TALLY" CONTROL OUT TRANSACTIONS
;CALL MOVE P3,SEL6(U) ;CONTAINS THE ERROR CODE
; PUSHJ P,CTOTLY ;TALLY THE CONTROL OUT TRANSACTION
; ERROR ;UNKNOWN CONTROL OUT CODE
; OK ;TRANSACTION TALLYED.
; ; P1 := "BIT MAP" WHICH HAS A SINGLE
; ; BIT SET ACCORDING TO THE CODE. THIS IS
; ; USEFULL FOR KDPCOO AND KDPCOI.
;RETURN "BIT MASK" IN P1
CTOTLY: MOVEI P1,(P3) ;COPY THE ERROR CODE FOR A WHILE
ANDI P1,377 ;MAKE SURE WE HAVE JUST THE ERROR CODE
CAIL P1,6 ;LOWEST ERROR CODE WE KNOW OF...
CAILE P1,26 ;IS THIS ERROR CODE "IN RANGE"
POPJ P, ; IF NOT IN RANGE, GIVE ERROR RETURN
LSH P1,-1 ;MAKE CODES DENSE (NOT EVERY OTHER)
MOVN T1,P1 ;COPY THE NEGATIVE OF THE CODE FOR LATER LSH
ADDI P1,KDLCTO-3(F) ;RELOCATE CODE TO COUNTER TABLE
AOS (P1) ;TALLY THE CONTROL OUT TRANSACTION
MOVSI P1,(1B0) ;GET A BIT SHIFTED 0 BITS
LSH P1,(T1) ;SHIFT IT BY "ERROR CODE" BITS
RETSKP ;GOOD RETURN
;FREBOI FREE AN INPUT BUFFER DESCRIPTOR LIST
; RETURN CANONICAL BYTE POINTER TO MESSAGE IN T4
FREBOI: PUSHJ P,GETBDL ;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 KMC SCREWED UP
MOVEI S,KDSNRB ;BIT DESIGNATING NEXT RECEIVE BUFFER
MOVEI T1,KDLRD1(F) ;ASSUME THAT THIS SHOULD BE BUFFER #1
TDNE S,KDLSTS(F) ; BUT IF IT SHOULD BE #2, THEN
MOVEI T1,KDLRD2(F) ; CHANGE OUR MIND.
CAMN T1,T4 ;MAKE SURE WE GOT THE CORRECT BUFFER BACK
SOSGE KDLRBC(F) ;DECREMENT THE NUMBER OF ACTIVE RECEIVE BUFFERS
POPJ P, ;++ EITHER WRONG BUFFER, OR TOO MANY. KILL LINE
XORB S,KDLSTS(F) ;TOGGLE THE NEXT BUFFER OUT BIT
MOVSI T1,(1B0) ;GET THE BDL IN-USE MARK BIT
TDNN T1,0(T4) ; AND MAKE SURE THAT IT'S SET.
;[DBUG] POPJ P, ;WE'VE SCREWED UP THE BDL POINTERS?
PUSHJ P,NTDSTP## ;WE'VE SCREWED UP THE BDL POINTERS [DBUG]
ANDCAM T1,0(T4) ;CLEAR THE IN-USE BIT
RETSKP
;RCVXNK - UTILITY ROUTINE TO SEND A NAK. CODE IN T1
RCVXNK: DPB T1,KDLXNK## ;SAVE THE NAK CODE
LDB T1,KDLSTA## ;GET THE STATE, AND
CAIE T1,KD%RUN ; IF WE ARE NOT RUNNING,
JRST RCVXN1 ; DON'T SEND A NAK
MOVSI T1,(KDSNAK) ;GET THE NEED TO NAK FLAG
IORM T1,KDLSTS(F) ;SET FLAG FOR XMTBUF TO SEE
MOVSI T1,(KDSACK) ;NO POINT IN ACKING...
ANDCAM T1,KDLSTS(F) ; WHEN WE'RE NAKING
RCVXN1: PUSHJ P,XMTBUF ;CALL XMTBUF TO TRY TO SEND THE NAK
PJRST RCVBUF ;TRY TO QUEUE ANOTHER RECEIVE REQUEST
;FREMAI ROUTINE TO FREE MAINT MESSAGES
; MAINT MESSAGES MUST BE FREED OUTSIDE THE KDPOFF/KDPON INTERLOCK WHICH
; IS NOT POSSIBLE IN XMTMAI.
;CALL F := KDL POINTER
;RETURN CPOPJ
FREMAI: LDB T1,KDLSTA## ;GET OUR STATE, AND
CAIE T1,KD%MAI ;IF WE AREN'T IN MAINT STATE
POPJ P, ;DON'T SCREW AROUND WITH THE QUEUE!!
FREMA1: KDPOFF ;INTERLOCK WRT XMTMAI
HRRZ T3,KDLWTA(F) ;GET THE NEXT MSG AWATING AN ACK
JUMPE T3,FREMA2 ; IF NONE, WE ARE DONE
HRRZ T2,MB.NXT(T3) ;GET THE "NEXT" MESSAGE
HRRZM T2,KDLWTA(F) ; AND MAKE IT THE NEW FIRST
SOSGE KDLCTA(F) ;COUNT DOWN ONE LESS MESSAGE
PUSHJ P,NTDSTP## ;SCREWED UP THE COUNTS...
KDPON ;GIVE BACK THE INTERLOCK
MOVEI T1,DC.IOD ;SAY OUTPUT DONE
PUSHJ P,CALDVR ; TO OUR FRIEND THE DRIVER
JRST FREMA1 ;SEE IF THEY ARE ANY MORE LEFT
;HERE IF NO MORE MAINT MSGS TO FREE
FREMA2: SKIPE KDLCTA(F) ;MAKE SURE THE COUNT IS ZERO
PUSHJ P,NTDSTP## ;STOP IF COUNT IS WRONG W.R.T. QUEUES
KDPON ;RE-ENABLE INTERRUPTS
POPJ P, ; AND WE ARE DONE
SUBTTL ERROR ROUTINES
;KDPERR - CALLED WHEN A KDP IS HOPELESSLY ILL.
;CALL JSP T1,KDPERR ;T1 CONTAINS CALLERS PC FOR DEBUGGING
;RETURN CPOPJ ; USUALLY TO CALLER'S CALLER
KDPERR: MOVEM T1,KDPCPC(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 KMC-11 error on /
PUSHJ P,PRKDP ;TELL HIM WHICH KDP IT WAS.
PUSHJ P,INLMES## ;TELL HIM WHO NOTICED
ASCIZ /, called/
MOVE T2,KDPCPC(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 / KMC-11 Halted.
/
PUSHJ P,KDPHLT ;STOP IT (CLEAR RUN)
MOVSI T1,(DF.RQK) ;NOW REQUEST THAT KDPLDR
IORM T1,DEBUGF## ; ON THE NEXT CLOCK TICK
POPJ P, ;RETURN (LET SOME ONE ELSE CLEAN UP...)
;KDLERR - ROUTINE CALLED WHEN AN EVENT FATAL TO ONLY ONE LINE OCCURS
;CALL PJSP T1,KDLERR ;T1 HAS CALLER'S PC
;RETURN POPJ P, ;USUALLY TO CALLER'S CALLER
KDLERR: MOVEM T1,KDLCPC(F) ;REMEMBER THE CALLERS PC
MOVE U,OPRLDB## ;GET THE OPER'S TTY POINTER
PUSHJ P,INLMES## ;TELL HIM THE BAD NEWS
ASCIZ /%% Fatal error on /
PUSHJ P,PRKDL ;TELL HIM WHICH LINE.
PUSHJ P,INLMES##
ASCIZ /, called/
MOVE T2,KDLCPC(F) ;GET THE CALLER'S PC BACK
SOS T2 ;BACK IT UP TO THE POINT OF CALL
PUSHJ P,PCP## ;PRINT THE PC
PUSHJ P,INLMES## ;FINISH OFF
ASCIZ / Line shut down.
/
;NOW SHUT DOWN THE LINE
PUSHJ P,KDLFLS ;START THE BUFFERS FLUSHING
MOVE T1,KDLMAP(F) ;GET THE FIRST MAP LOCATION
MOVEI T2,KDLPPL ;PAGES PER LINE
SETZ T3, ;A ZERO TO ZAP THE MAP WITH
SOJGE T2,[WRIO T3,(T1);ZAP THE PAGE
AOJA T1,.] ;AND LOOP OVER THEM ALL
PUSHJ P,KDLPDN ;TELL THE DRIVER HIS LINE IS DOWN
PJRST CLBFRS ;CLEAR THE BUFFER USE BITS AND RETURN
;KDLFLS ROUTINE TO SET THE "FLUSH IN PROGRESS" BITS, GO TO "FLUSH" STATE
; AND QUEUE FLUSH TRANSACTIONS TO THE KMC-11
KDLFLS: MOVEI T1,KD%FLS ;GET THE "FLUSHING BUFFERS" STATE
DPB T1,KDLSTA## ; AND USE SET OUR STATE TO THAT
MOVEI T1,KDSXFL!KDSRFL;GET THE "FLUSH IN PROGRESS BITS"
IORM T1,KDLSTS(F) ; AND SET THEM IN THE STATUS WORD
HRRZ T1,KDLINE(F) ;GET OUR LINE NUMBER
LSH T1,10 ; AND PUT IT IN BSEL3
MOVEI T2,BFRKIL ;GET THE KILL BIT (LEAVE BDL ADDR = 0)
MOVE W,KDLKDP(F) ;JUST MAKE SURE "W" IS SET UP
PUSHJ P,KMCINU ;MASSACRE THE OUTPUT BUFFERS
PJSP T1,KDPERR ;?? KMC-11 CRASHED WHILE WE WEREN'T LOOKING ??
IORI T1,KMCIOT ;SET "INPUT" BUFFER BIT
PUSHJ P,KMCINU ;MURDER ALL THE INPUT BUFFERS AS WELL.
PJSP T1,KDPERR ;?? "" ??
POPJ P, ;ALL DONE
;KDPHLT - ROUTINE TO CRASH A KMC-11 (KDP) WHEN SOMETHING IS DRASTICALLY
; WRONG.
KDPHLT: PUSHJ P,SAVE1## ;WE USE 1 P
MOVEI T1,KMCMCL ;GET THE MASTER CLEAR BIT
WRIO T1,@KDPCSR(W) ;CLEAR RUN, SET MASTER CLEAR. (DIE DIE DIE...)
MOVSI T1,(KDPSRU) ;GET MICROCODE RUNNING BIT
ANDCAM T1,KDPSTS(W) ;AND CLEAR IT SO WE WON'T TRY TO RESTART
MOVE T2,KDPCSR(W) ;GET ADDRESS OF THE KMC-11 CSR'S
MOVE T3,[POINT 18,KDPRGS(W)] ;GET A BYTE POINTER TO "CRASH REGS"
MOVEI T1,4 ;GET COUNT OF REGISTERS TO READ
KDPHL1: RDIO S,(T2) ;GET KMC-11 REGISTER
IDPB S,T3 ; AND STORE IT IN THE KDP BLOCK FOR DEBUGGING
ADDI T2,2 ;GO TO THE NEXT -11 WORD
SOJG T1,KDPHL1 ;LOOP OVER ALL REGISTERS
;NOW CLEAR THE "VOLITILE" PART OF THE KDP BLOCK
HRLI T1,KDPZER(W) ;GET ADDR OF FIRST WORD TO ZERO
HRRI T1,KDPZER+1(W) ;GET ADDRESS OFF THE SECOND
SETZM KDPZER(W) ;CLEAR THE FIRST WORD
BLT T1,KDPLEN-1(W) ;CLEAR THE REST
;NOW DECLARE ALL LINES ASSOCIATED WITH THIS KDP AS CRASHED.
MOVE P1,KDPDPN(W) ;GET A COUNT OF THE NUMBER OF DUP-11 ATTACHED
KDPHL2: SOJL P1,CPOPJ## ;COUNT DOWN EACH DUP-11 PROCESSED
MOVE T1,P1 ;GET OUR LINE NUMBER
IMULI T1,KDLPPL ; TIMES NUMBER OF PAGES PER LINE
ADD T1,KDPIMR(W) ; RELOCATE TO THE FIRST PAGE
MOVEI T2,KDLPPL ;GET THE NUMBER OF PAGES TO CLEAR
SETZ T3, ;GET A ZERO
SOJGE T2,[WRIO T3,(T1);ZERO THE NEXT WORD
AOJA T1,.] ;AND LOOP OVER ALL PAGES FOR THIS LINE
MOVEI F,(P1) ;COPY OFFSET INTO LINE TABLE
ADDI F,KDPKDL(W) ;RELOCATE OFFSET TO LINE TABLE
SKIPN F,(F) ;LOAD ADDRESS OF KDL BLOCK
JRST KDPHL2 ;ONCE SAID THERE WASN'T ONE, LOOP OVER REST
LDB T1,KDLSTA## ;GET THE STATE OF THIS LINE
MOVEI T2,KD%DWN ;GET THE "DOWN" STATE
DPB T2,KDLSTA## ; AND MARK THIS LINE AS HOPELESS
CAILE T1,KD%FLS ;WAS LINE "ALIVE"
PUSHJ P,KDLPDN ; IF IT WAS UP, REQUEUE PCB'S AND CALL NETSER
JRST KDPHL2 ;LOOP OVER ALL LINES
SUBTTL PROTOCOL CONTROL SUBROUTINES
;KDLPUP - ROUTINE TO TELL OUR DRIVER THAT PROTOCOL IS UP
KDLPUP: PUSH P,T4 ;CALLER EXPECTS US TO SAVE T4
MOVEI T1,DC.IPU ;GET THE PROTOCOL "UP" FUNCTION
PUSHJ P,CALDVR ; AND TELL THE DRIVER THE GOOD NEWS
POP P,T4 ;RESTORE CALLER'S T4
POPJ P, ;AND WE'RE DONE
;KDLPDN - ROUTINE TO RETURN ALL UNSENT BFR'S AND THEN DECLARE THE
; LINE DOWN
; HERE EITHER FROM CLOCK LEVEL (TIMED OUT), OR FROM RECEIVE
; INTERRUPT LEVEL (MAINT/START MSG), OR CONTROLLER REQUEST
; FROM A DRIVER (ROUTER KF.IN OR KF.HA)
KDLPDN: PUSHJ P,KDLPD0 ;EMPTY THE QUEUES FIRST
;HERE WHEN WE'VE RETURNED ALL THE MESSAGES. DECLARE PROTOCOL DOWN
MOVEI T1,DC.IPD ;GET THE "PROTOCOL DOWN" FUNCTION
PUSHJ P,CALDVR ;TELL THE DRIVER THE BAD NEWS
PUSHJ P,CLCTRS ;CLEAR THE DDCMP MESSAGE NUMBER COUNTERS
POPJ P, ;AND WE'RE DONE
KDLPD0: KDPOFF ;NO INTERRUPTS WHILE HACKING QUEUES
HRRZ T3,KDLWTA(F) ;GET THE FIRST BFR ON THE WTA QUEUE
JUMPE T3,KDLPD1 ;IF QUEUE IS EMPTY, TRY WTO
HRRZ T2,MB.NXT(T3) ;GET THE "NEXT" BFR ON THE QUEUE
HRRZM T2,KDLWTA(F) ; AND MAKE IT THE FIRST
SOSGE KDLCTA(F) ;COUNT DOWN ONE LESS WTA BFR
PUSHJ P,NTDSTP## ;IF IT WENT NEGATIVE, DUMP IT.
JRST KDLPD2 ;GO RETURN THE BUFFER
;HERE IF THE WTA QUEUE IS EMPTY, TRY WTO
KDLPD1: SKIPE KDLCTA(F) ;MAKE SURE THE WTA QUEUE COUNTED OUT RIGHT
PUSHJ P,NTDSTP## ;STOP IF COUNT AND QUEUE DON'T AGREE
HRRZ T3,KDLWTO(F) ;GET THE FIRST BFR ON THE WTO QUEUE
JUMPE T3,KDLPD3 ;IF NONE, WE'VE RETURNED ALL THE BFRS
HRRZ T2,MB.NXT(T3) ;GET THE "SECOND" BUFFER ON THE WTO QUEUE
HRRZM T2,KDLWTO(F) ;AND MAKE IT THE FIRST BUFFER
;HERE TO RETURN A MESSAGE AS "OUTPUT NOT DONE"
KDLPD2: KDPON ;WE'VE GOT OUR BFR, RE-ENABLE INTERRUPTS
MOVEI T1,DC.IOF ;GET THE "OUTPUT NOT DONE" FUNCTION
PUSHJ P,CALDVR ;AND TELL OUR DRIVER THE NEWS
JRST KDLPD0 ;LOOP TILL WE HAVE RETURNED ALL BFRS
KDLPD3: KDPON ;RE-ENABLE INTERRUPTS
POPJ P, ;AND RETURN
;KDLINI - ROUTINE TO INITIALIZE A KDL-PAGE
;CALL MOVX F,KDL-BLOCK ADDRESS
; PUSHJ P,KDLINI ;FROM CLOCK OR UUO LEVEL
;RETURN POPJ P, ;ALWAYS
; THIS ROUTINE RE-BUILDS A KDL BLOCK. IN PARTICULAR IT:
; A) SETS UP ALL IMPORTANT "STATIC" LOCATIONS IN THE KDL PAGE
; B) MAPS THE KDL PAGE SO AS TO MAKE IT ACCESSIBLE TO THE KMC-11
; C) QUEUES THE BASE IN AND CONTROL IN TRANSACTIONS NECESSARY
; TO LET THE LINE SEND AND RECEIVE MSGS
KDLINI: MOVEI T1,KDPMAX## ;FIRST FIND THE KDP THAT OWNS THIS KDL
KDLIN1: SKIPGE T1 ;IF WE COULDN'T FIND ONE
PUSHJ P,NTDSTP## ; THEN A DRIVER GAVE US A BAD KDL ADDRESS
HRRZ W,KDPTBL##(T1) ;ASSUME THAT THIS IS THE RIGHT KDP
HRRZ T2,KDPDPN(W) ;GET THE NUMBER OF DUPS ON THIS ONE
MOVEI T3,KDPKDL(W) ;GET THE ADDRESS OF THE DUP TABLE
KDLIN2: SOJL T2,[SOJA T1,KDLIN1] ;IF NO MORE DUPS, DO NEXT KDP
HRRZ T4,(T3) ;GET THE RH OF THE NEXT ENTRY
CAIE T4,(F) ;SEE IF THIS IS THE ONE
AOJA T3,KDLIN2 ;IF NOT, KEEP LOOKING
SETZM KDLFZR(F) ;START CLEARING AFTER THE BLOCK POINTERS
HRLI T3,KDLFZR(F) ;FIRST HALF OF "BLT" POINTER
HRRI T3,KDLFZR+1(F) ;SECOND HALF OF "BLT" POINTER
BLT T3,KDLLEN-1(F) ;CLEAR THE BLOCK
HRRZM W,KDLKDP(F) ;STORE THE KDP ADDRESS
MOVE T1,KDPDPN(W) ;GET THE NUMBER OF DUPS ON THIS KMC
SUBI T1,1(T2) ;GET THE NUMBER OF THIS ONE
MOVEM T1,KDLINE(F) ;GET OUR LINE NUMBER
IMULI T1,KDLPPL ;OFFSET BY THE NUMBER OF PAGES PER LINE
ADD T1,KDPIMR(W) ;CALCULATE OUR MAP REGISTER'S ADDRESS
MOVEM T1,KDLMAP(F) ;STORE THE MAP REGISTER'S ADDRESS
MOVEI T1,KDPKDL(W) ;GET ADDR OF FEK TO KDL CORRISPONDENCE TBL
ADD T1,KDLINE(F) ; THAT LIVES IN THE KDP BLOCK
HLRZ T1,(T1) ;GET OUR FEK ADDRESS (IF RUNNING ANF)
MOVEM T1,KDLFEK(F) ; AND STORE THAT
MOVEI T1,M.DN60## ;SEE IF DN60 WAS LOADED
JUMPE T1,KDLIN3 ; IF NOT, THEN THERE IS NO DLX FOR THIS LINE
MOVE T1,KDLINE(F) ;IF THERE IS A DLX, GET THE LINE NUMBER
MOVE T1,KDPBAS##(T1) ; AND FROM THAT, THE DLX TABLE ADDRESS
KDLIN3: MOVEM T1,KDLDLX(F) ;AND STORE A POINTER TO THE DLX BLOCK
MOVE T1,KDLINE(F) ;GET OUR LINE NUMBER BACK
IMULI T1,KDLPPL ;MULTIPLY BY THE NUMBER OF PAGES PER LINK
LSH T1,^D11 ;CONVERT PAGE OFFSET TO BYTE OFFSET
ADD T1,KDPIEA(W) ;CALCULATE OUR PAGE ADDRESS
MOVEM T1,KDLADR(F) ;STORE OUR PAGE'S -11 ADDRESS
MOVE T1,KDLINE(F) ;GET OUR DUP-11'S NUMBER AGAIN
LSH T1,3 ;MULTIPLY BY THE SIZE OF A DUP-11 (IE. 8)
ADD T1,KDP1DP(W) ;RELOCATE BY ADDRESS OF THE FIRST DUP-11
MOVEM T1,KDLCSR(F) ;STORE OUR CSR ADDRESS (FOR BASE IN)
;KDL PAGE NOW INITIALIZED. TIME TO SET UP THE MAP.
MAP T1,(F) ;GET THE PHYSICAL ADDRESS OF THE KDL PAGE.
AND T1,[XWD 17,-1] ;GET JUST THE ADDRESS
LSH T1,W2PLSH## ;CHANGE ADDRESS TO PAGE NUMBER
IORI T1,UNBVBT!UNBD18;VALID & 18 BIT DISABLE
MOVEI T2,KDLPPL ;GET A COUNT OF THE NUMBER OF PAGES PER LINE
MOVE T3,KDLMAP(F) ;GET THE ADDRESS OF THE MAP
SOJGE T2,[WRIO T1,(T3);WRITE THE PAGE REGISTER
AOS T1 ;GET READY TO WRITE THE NEXT ONE
AOJA T3,.] ;LOOP OVER ALL REGISTERS
HRRZ T1,KDLINE(F) ;GET LINE NUMBER FOR "BASE IN"
LSH T1,10 ;SHIFT INTO UPPER BYTE
IORI T1,BASEIN ;SET THE FUNCTION CODE
HRRZ T2,KDLCSR(F) ;GET THE DUP-11'S CSR ADDRESS (-11 MEMORY)
ANDI T2,17777 ; BUT ONLY USE LOW 13 BITS OF IT.
PUSHJ P,KMCINU ;QUEUE BASE-IN TRANSACTION
JSP T1,KDPERR ;?? KMC-11 CRASHED ?? GIVE KMC NOT RUNNING ??
TRZ T1,2 ;CHANGE BASE-IN FUNCTION TO CONTROL-IN (UGLY)
MOVEI T2,CDDCMP!CENABL;SET POLLING = 0, DDCMP MODE, AND ENABLE
PUSHJ P,KMCINU ;QUEUE THE CONTROL-IN TRANSACTION
JSP T1,KDPERR ;?? KMC-11 CRASHED ?? GIVE KMC NOT RUNNING ??
POPJ P, ;KDL HAS BEEN INITIALIZED
;CALDVR ROUTINE TO CALL THIS LINE'S DRIVER
;CALL F := POINTER TO A KDL
; SETS UP T2 := DRIVER'S BLOCK ADDRESS
; AND DISPATCHES TO THE DRIVER ROUTINE
;DON'T PRESERVE F/W SINCE OUR DRIVER SHOULDN'T CLOBBER THEM!
DEFINE X(TYP,CON,DAT,ADR),<
IFN .-CALDV0-<2*DD.'TYP>,<
PRINTX ? CALDVR vector dispatch phase error for TYP user>
IFE CON,<
PUSHJ P,NTDSTP## ;;DIE IF NO DRIVER SUPPORTED
HALT .> ;;FILLER FOR TWO-WORD VECTOR
IFN CON,<
MOVE T2,DAT(F) ;;GET "DRIVER" DATA BLOCK ADDRESS
PJRST ADR> ;;AND GO TO APPROPRIATE DRIVER
> ;END DEFINE X
CALDVR: PUSHJ P,SAVR## ;DECnet USES R AS A TRASH AC.
HRRZ T2,KDLUSR(F) ;GET THE USER CODE
CAILE T2,DD.MAX ; AND RANGE CHECK IT
PUSHJ P,NTDSTP## ;++ ILLEGAL USER CODE
LSH T2,1 ;TRANSLATE INTO TWO-WORD BLOCK OFFSET
PJRST .+1(T2) ;DISPATCH BASED ON "USER" TYPE
CALDV0: X (NOBODY,1,0,NOBDSP##)
CALDV1: X (ANF10,FTNET,KDLFEK,D8KK2D##)
CALDV2: X (DECNET,FTDECN,KDLLBK,CALPPI)
CALDV3: X (PROGRAM,1,KDLDDB,KDPIDS##)
CALDV4: X (IBMCOMM,FTDN60,KDLDLX,D6KK2D##)
IFN .-CALDV0-<2*<DD.MAX+1>>,<
PRINTX ? CALDVR vector dispatch entry missing>
IFN FTDECN,<
CALPPI: DNCALL KDIPPI## ;CALL DECNET IN DECNET CONTEXT
POPJ P, ;AND RETURN
PJRST CPOPJ1## ;...
>; END IFN FTDECN
;CLCTRS CLEAR THE VARIOUS COUNTERS ASSOCIATED WITH A LINE
CLCTRS: SETZ T1, ;GET A ZERO
DPB T1,KDLTIM## ;CLEAR THE TIMER
DPB T1,KDLRMN## ;CLEAR THE RECEIVE MESSAGE NUMBER
DPB T1,KDLLMX## ;CLEAR LAST MESSAGE SENT
DPB T1,KDLLMA## ;CLEAR LAST MESSAGE ACKED
DPB T1,KDLRPC## ;CLEAR THE REP COUNTER
POPJ P,
;CLBFRS ROUTINE TO SET ALL BUFFERS TO "EMPTY". USED WHEN GOING INTO
; MAINT MODE.
CLBFRS: MOVEI T1,KDSNRB!KDSNXB!KDSRFL!KDSXFL ;STATUS BITS TO CLEAR
ANDCAM T1,KDLSTS(F) ;MAKE SURE NEXT BUFFER IS FIRST BUFFER.
HRLI T1,KDLXBC(F) ;START ZEROING HERE
HRRI T1,KDLXBC+1(F) ; ...
SETZM KDLXBC(F) ;WRITE THE "SEED" ZERO
BLT T1,KDLLEN-1(F) ; AND ZAP THE REST OF THE BLOCK
POPJ P, ;RETURN WITH ALL BUFFERS AND USE BITS CLEAR
;STPTMR ROUTINE TO STOP THE DDCMP TIMER
STPTMR: SETO T1, ;STOP TIMER
HRRZ T2,KDLUSR(F) ;CHECK FOR ANF CROCK
CAIN T2,DD.ANF ;BECAUSE IT WANTS TO
SETZ T1, ;IDLE REPS, SO WE JUST RESTART TIMER
DPB T1,KDLTIM## ;WHATEVER
POPJ P,
;ZRCTRS - ROUTINE TO ZERO THE COUNTER SECTION OF THE KDL BLOCK
;CALL MOVX F,KDL-BLOCK ADDRESS
; PUSHJ P,ZRCTRS
;RETURN CPOPJ ;ALWAYS
ZRCTRS: HRLI T1,KDLZER(F) ;NOW ZERO COUNTER SECTION OF THE KDL (RESTART)
HRRI T1,KDLZER+1(F) ;GET ADDRESS OF SECOND WORD ...
SETZM KDLZER(F) ;CLEAR THE FIRST ONE
BLT T1,KDLLEN-1(F) ;CLEAR THE REST
POPJ P, ;AND WE'RE DONE
SUBTTL PRINT ROUTINES
;PRKDP - ROUTINE TO PRINT OUT "KDP #?"
PRKDP: PUSHJ P,INLMES## ;PRINT OUT THE ASCII PART
ASCIZ /KDP #/
MOVE T1,KDPNUM(W) ;GET THE KDP'S NUMBER
PJRST PRTDIG## ; AND PRINT THAT
;PRKDL - ROUTINE TO PRINT "KDP #?, KDL #?"
PRKDL: PUSHJ P,PRKDP ;PRINT OUT THE FIRST PART
PUSHJ P,INLMES## ;PRINT OUT THE ", KDL #"
ASCIZ /, KDL #/
MOVE T1,KDLINE(F) ;GET OUR DUP-11 LINE NUMBER
PJRST PRTDIG## ; AND PRINT THAT.
;ROUTINE TO SAVE F & W FOR THE KDP??? 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
POINT 8,1(T4),17 ;FIFTH BYTE
POINT 8,1(T4),9 ;SIXTH BYTE
POINT 8,1(T4),35 ;SEVENTH BYTE
POINT 8,1(T4),27 ;EIGHTH 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
; XLIST
; $LIT ;DON'T LIST LITERALS
LIST
KDPEND::END