TITLE NULFEK - LOCAL NETWORK "FRONT END" SERVICE ROUTINE - V007 SUBTTL WEM/ 22 JAN 80 SEARCH F,S,NETPRM $RELOC $HIGH ;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED ; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE. ; .CPYRT< COPYRIGHT (C) 1978,1979,1980,1982,1984 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS. > XP VNLFEK,007 ;PUT VERSION NUMBER IN GLOB AND LOADER MAP NULFEK::ENTRY NULFEK COMMENT @ The "null" FEK is intended to behave the same as any normal FEK except for the fact that any "output" messages it is given are returned as "input" messages. This allows the -10 to talk to itself on lonely nights when the operators have gone home. Currently it's only use is in the implementation of local task's. Whether of not this method is smaller than the local tasks of yesteryear may be debated. It's certianly slower. It has however, the following redeeming features. a) Task to task may be completly debugged on a singly -10 cpu. (Even the fancy connect processing) b) Common code is used as much as possible. This means that local tasks won't get broken every week. c) I don't have to worry about the case of two 100k user jobs trying to talk to each other via local tasks on a 256k machine. (As far as I can tell trying to lock both jobs in core would simply hang the system...) A few things should be mentioned about the code. First, the code attempts to perform the proper byte conversion and data compression operations normally delegated to the front-end -11. These functions will also be required by the KS-10 project. Steal this code. Second, one can run the "null" FEK as fast as one wants simply by putting the call go "NLFCPY" in the approiate place. I expect that the optimal place is in the "NLFRDD" and "NLFWRT" routines for maximum through put, though for controlled debugging, I suggest some less synchronous place such as a clock interrupt. @ SUBTTL 1.0 NULL FEK ENTRY POINTS. (VECTORED HERE BY THE FEK BLOCK) NLFDSP::CAIL T1,FF.ONC ;RANGE CHECK THE FUNCTION CODE CAILE T1,FF.STC ; AND IF IT'S OUT OF RANGE STOP ; (WE DON'T WAKE OR SLEEP.) PUSHJ P,NTDSTP## ;++ ERROR: FUNCTION CODE OUT OF RANGE JRST @.+1(T1) ;DISPATCH ON FUNCTION CODE JRST NLFONC ;ONCE ONLY CODE JRST NTFSEC## ;ONCE/SECOND CODE (USE NETSER'S DEFAULT) JRST NLFRDD ;READ REQUEST JRST NLFWRT ;WRITE REQUEST JRST NTDSTP## ;CRASH. SHOULD NEVER HAPPEN JRST NTDSTP## ;DOWN. SHOULD NEVER HAPPEN JRST NTDSTP## ;UP. SHOULD NEVER HAPPEN JRST CPOPJ## ;STATION CONTROL (WE'RE NOT INTERESTED) NLFONC: MOVSI T1,FK.ONL!FK.NID!FK.NUL ;GET THE BITS THAT SYSINI CLEARS IORB T1,FEKBLK(J) ;AND SET THEM SO THIS FEK IS "ALIVE" POPJ P, ;THAT'S ALL NLFRDD: AOS FEKBSI(J) ;MARK "INPUT ACTIVE" MOVSI T1,FK.STO ;GET THE "KICK OUTPUT BIT" IORM T1,FEKBLK(J) ; AND TELL CLOCK LEVEL TO TRY OUTPUT POPJ P, ;WE DON'T CALL NLFCPY HERE BECAUSE ; WE WOULD BUILD THE STACK UP TOO HIGH NLFWRT:;PJRST NLFCPY ;CALL THE COPY ROUTINE ;NLFCPY ROUTINE CALLED TO PROCESS ONE OUTPUT PCB AND COPY IT IN TO ONE ; INPUT PCB, FINALLY CALLING FEKINT TWICE SO THAT NETSER NOTICES. ;CALL MOVEI J,FEK ; PUSHJ P,NLFCPY ;RETURN CPOPJ ;AFTER CALLING "FEKINT" ; ;REGISTER USAGE FOR THIS IS ; S := USUALLY USED TO PASS THE NEXT CHAR TO BE COPIED ; T1 := THE COUNT OF CHARS TO TAKE FROM THE "OUTPUT" PCB ; T2 := THE BYTE POINTER USED IN REMOVING CHARS FROM THE "OUTPUT" PCB ; T3 := THE COUNT OF CHARS PUT IN THE "INPUT" PCB ; T4 := THE BYTE POINTER USED TO PUT CHARS IN THE "INPUT" PCB ; P1-P3 :=LOCAL STORAGE FOR THE ROUTINES THAT MUST TO BYTE PACKING. ; U := POINTER TO THE "OUTPUT" PCB (THE ONE WE READ DATA FROM) ; W := POINTER TO THE "INPUT" PCB (THE ONE WE WRITE DATA TO) ; J := POINTER TO THE FEK (AT ENTRY), OR RETURN ADDR FOR "QUICKIES" ; NLFCPY::JSP T1,SETUP ;PUSH W, U, S AND SET UP S NLFCP1: AOSE FEKBSO(J) ;TRY TO GET THE OUTPUT INTERLOCK JRST [MOVSI T1,FK.STO;IF THE OTHER CPU IS COPYING, IORM T1,FEKBLK(J); THEN MAKE SURE HE LOOKS AT US LATER JRST EXIT] ; AND LEAVE. (1 CPU'S ENOUGH) SKIPLE FEKOCT(J) ;MAKE SURE THERE IS OUTPUT TO GO SKIPGE FEKBSI(J) ; AND MAKE SURE WE'VE GOT AN INPUT BUFFER JRST [SETOM FEKBSO(J);IF WE CAN'T COPY NOW, CLEAR OUTPUT ACTIVE JRST EXIT] ; AND EXIT HRRZ U,FEKOAD(J) ;GET THE OUTPUT (WE READ FROM IT) PCB HRRZ W,FEKIAD(J) ;GET THE INPUT (WE WRITE TO IT) PCB SETZ T3, ;WE HAVE COPIED NO BYTES TO THE INPUT PCB MOVE T4,PCBIAD(W) ;BUT THIS IS WHERE WE'LL PUT 'EM WHEN WE DO. HRRZ T1,PCBOCT(U) ;THE COUNT OF THE NUMBER OF CHARS IN THE HEADER MOVE T2,PCBOAD(U) ;BYTE POINTER TO THE HEADER SECTION CPY1: SOJL T1,CPY2 ;COUNT DOWN THE HEADER CHARS ILDB S,T2 ;GET THE NEXT BYTE IDPB S,T4 ;STORE IT IN THE "INPUT" PCB AOJA T3,CPY1 ;LOOP OVER ALL HEADER CHARS CPY2: LDB T1,PCBPCV## ;GET THE CONVERSION CODE CAIL T1,PCV.NC ;RANGE CHECK CAILE T1,PCV.BN ; IT BEFORE DISPATCHING OFF OF IT PUSHJ P,NTDSTP## ;++ CONVERSION CODE OUT OF RANGE PUSHJ P,@[EXP C.NC,C.LP,C.BN](T1) ;DISPATCH TO PROPER COPY ROUTINE HRRM T3,PCBICT(W) ;STORE THE COUNT OF CHARS COPIED NETOFF ;NO INTERRUPTS WHILE HACKING QUEUES HRRZM U,FEKODN(J) ;STORE THE EMPTIED PCB HRRZ U,PCBBLK(U) ;GET POINTER TO "NEXT" PCB TO BE OUTPUT HRRZM U,FEKOAD(J) ; AND MAKE IT "FIRST" SOS FEKOCT(J) ;DECREMENT THE NUMBER OF PCB'S TO BE OUTPUT NETON ;INTERRUPTS OK NOW SETOM FEKBSI(J) ;CLEAR INPUT ACTIVE MOVEI T1,FI.RDD ;GET THE "INPUT DONE" BIT PUSHJ P,FEKINT## ;LET NETSER GROVEL ON RECYCLED DATA MOVEI T1,FI.ODN ;GET THE "OUTPUT DONE" BIT PUSHJ P,FEKINT## ;LET NETSER CALL US BACK WITH A NEW OUTPUT PCB SETOM FEKBSO(J) ;CLEAR OUTPUT ACTIVE JRST NLFCP1 ;GO SEE IF THERE ARE ANY MORE TO DO ;C.NC ROUTINE TO DO A QUICK "NO COMPRESSION" COPY C.NC: HRRZ T1,PCBOC1(U) ;GET THE SECONDARY BYTE COUNT MOVE T2,PCBOA1(U) ;GET THE SECONDARY BYTE POINTER C.NC1: SOJL T1,CPOPJ## ;LOOP OVER ALL CHARACTERS ILDB S,T2 ;GET THE NEXT 8 BIT CHAR IDPB S,T4 ;AND STORE IT IN THE "INPUT" MESSAGE AOJA T3,C.NC1 ;KEEP IT UP UNTIL ALL CHARS COPIED ;C.LP ROUTINE TO SEND THE SECONDARY DATA OF A PCB WITH LINE PRINTER ; COMPRESSION ; ;DATA FOR LINE PRINTER IS COMPRESSED AS FOLLOWS: ; 1CCCCCCC CCCCCCC IS CHARACTER ; 01XXXXXX XXXXXX IS NUMBER OF BLANKS ; 001XXXXX XXXXX IS REPETITION FOR FOLLOWING CHAR ; ;REGISTER USAGE IS ; P1 := REPEAT COUNT ; P2 := CHAR BEING REPEATED ; P3 := NEXT CHAR (READ HERE AND COMPARED WITH P2) ; C.LP: JSP T1,SAVE4 ;PUSH J, P1, P2, P3 HRRZ T1,PCBOC1(U) ;GET THE SECONDARY BYTE COUNT MOVE T2,PCBOA1(U) ;GET THE BYTE POINTER SETZ P1, ;INITIALIZE THE REPEAT COUNT SOJL T1,RTN4X ;COUNT OFF THE FIRST BYTE ILDB P2,T2 ;READ THE FIRST BYTE FOR COMPARISON LPLOOP: SOJL T1,NMATCH ;COUNTING DOWN IS THE SAME AS NO-MATCH ILDB P3,T2 ;GET THE NEXT BYTE CAIN P2,(P3) ;IS IT THE SAME AS THE LAST? AOJA P1,LPLOOP ;IF SO, COUNT IT AND CONTINUE NMATCH: JUMPE P1,SINGLE ;IF P1 = 0, THEN ONLY ONE CHAR TO SEND AOS P1 ;ACCOUNT FOR THE FIRST CHAR CAIN P2," " ;ARE WE COMPRESSING SPACES? JRST SPACES ;IF SO. TREAT THEM ESPECIAL CHAR: CAIG P1,37 ;WAS THIS REPEATED TOO MANY TIMES FOR ONE BYTE JRST CHARX ;NO. WE CAN SEND IT AT ONCE MOVEI S,77 ;MAXIMUM REPEAT COUNT JSP J,WRITE8 ;SEND THE COUNT MOVEI S,(P2) ;GET THE CHARACTER WE REPEATED JSP J,WRITE8 ;AND SEND THAT SUBI P1,37 ;INDICATE THAT WE HAVE SEND SOME JRST CHAR ;GO BACK AND SEE IF IT WAS ENOUGH CHARX: MOVEI S,40(P1) ;GET THE REPEAT COUNT JSP J,WRITE8 ;SEND THAT MOVEI S,(P2) ;GET THE CHARACTER JSP J,WRITE8 ;AND SEND THAT JRST ADVNCE ;COPY P2 := P3, CLEAR P1 AND LOOK FOR MORE. SPACES: CAIG P1,77 ;MORE SPACES THAN WE CAN COMPRESS IN 1 BYTE JRST SPACEX ;IF NOT. SEND THE COUNT AND GO BACK TO LOOP MOVEI S,177 ;WE CAN SEND 77 IN ONE BYTE. JSP J,WRITE8 ;SEND THEM SUBI P1,77 ;ACCOUNT FOR THE SPACES SENT JRST SPACES ;AND TRY TO SEND MORE SPACEX: MOVEI S,100(P1) ;GET THE "SPACE" BIT AND THE COUNT JSP J,WRITE8 ;SEND THE SPACES JRST ADVNCE ;ADVANCE THE CHARACTERS AND READ SOME MORE SINGLE: MOVEI S,200(P2) ;GET THE "SINGLE CHAR" BIT JSP J,WRITE8 ;SEND THE CHARACTER ADVNCE: SETZ P1, ;CLEAR THE REPEAT COUNT MOVEI P2,(P3) ;ADVANCE THE CHARACTER TO MATCH JUMPG T1,LPLOOP ;GO BACK TO LOOP IF THERE ARE MORE CHARS TO DO JRST RTN4X ;OTHERWISE RESTORE THE STACK AND POPJ ;*** FOOTNOTE *** COMMENT \ Currently the line-printer compression code does not work. The code must be made smart enough to fixup the "CNT" field for the message whose data it is compressing. This involves re-parsing the NCL header and poking in the new "CNT" (Which is NEVER longer. Either shorter or the same length) \ ;C.BN ROUTINE TO DO A "BINARY" (IE 12 BIT) COPY. ; THE BASIC IDEA IS TO USE FANCY BYTE MOTION TO PLACE THE 4 BIT SUB-BYTES C.BN: JSP T1,SAVE4 ;PUSH J, P1, P2, AND P3 HRRZ T1,PCBOC1(U) ;GET THE SECONDARY BYTE COUNT (12 BIT BYTES) MOVE T2,PCBOA1(U) ;GET THE SECONDARY BYTE POINTER. C.BN1: SOJL T1,RTN4X ;IF ALL DONE. RETURN 4 AND POPJ ILDB P1,T2 ;GET THE FIRST 12 BITS LDB S,[POINT 8,P1,31] ;SEND THE FIRST 8 BITS (LEAVE 4) JSP J,WRITE8 SOJL T1,C.BNX ;IF WE COUNT OUT NOW WE HAVE 4 LEFT. ILDB P2,T2 ;GET SECOND 12 BITS 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 IN FROM OLD 12 BIT BYTE JSP J,WRITE8 ;SEND THE 8 BITS LDB S,[POINT 8,P2,35] ;GET THE LAST 8 BITS FROM NEW 12 JSP J,WRITE8 ;SEND THOSE JRST C.BN1 ;LOOP UNTIL "OUTPUT" IS EXHAUSTED C.BNX: LDB S,[POINT 4,P1,35] ;HERE WE HAVE 4 BITS LEFT OVER. GET THEM IN S LSH S,4 ;MAKE THEM HIGH ORDER BITS. FILL WITH ZEROS JSP J,WRITE8 ;SEND THE BYTE JRST RTN4X ;POP STACK AND POPJ ;UTILITY ROUTINES USED BY THE NULL FEK ;WRITE8 ROUTINE TO OUTPUT 8 BITS TO THE "INPUT" PCB ;CALL MOVE S,CHAR ; JSP J,WRITE8 ;RETURN JRST (J) ; WRITE8: IDPB S,T4 ;STORE THE CHARACTER AOJA T3,(J) ;COUNT IT AND RETURN ;SETUP SAVE 3 REGISTERS. SETUP S AND RETURN ;CALL JSP T1,SETUP ;RETURN JRST (T1) ; SETUP: PUSH P,W ;SAVE THE NDB POINTER (OR WHATEVER) PUSH P,U ;SAVE THE PCB POINTER PUSH P,S ;SAVE THE STATUS REGISTER HLLZ S,FEKBLK(J) ;GET THE FLAGS JRST (T1) ;EXIT RESTORE THE STACK AND POPJ ; EXIT: POP P,S ;RESTORE THE STATUS POP P,U POP P,W POPJ P, ;SAVE4 ROUTINE TO SAVE 4 COMMONLY USED REGISTERS ;CALL JSP T1,SAVE4 ; SAVE4: PUSH P,J PUSH P,P1 PUSH P,P2 PUSH P,P3 JRST (T1) ;RTN4X RESTORE THE 4 REGIXTERS AND EXIT (POPJ) ; RTN4X: POP P,P3 POP P,P2 POP P,P1 POP P,J POPJ P, XLIST ;DON'T LIST LITERALS $LIT LIST END