Trailing-Edge
-
PDP-10 Archives
-
tops20_version7_0_monitor_sources_clock
-
monitor-sources/niusr.mac
There are 19 other files named niusr.mac in the archive. Click here to see a list.
; UPD ID= 8568, RIP:<7.MONITOR>NIUSR.MAC.3, 11-Feb-88 11:43:23 by GSCOTT
;TCO 7.1218 - Update copyright date.
; *** Edit 7454 to NIUSR.MAC by RASPUZZI on 16-Apr-87, for SPR #21586
; Prevent GLFNF BUGHLTs, SUMNR1 BUGCHKs and UNBFNF BUGCHKs.
; Make .NION and .NIOFF global.
; *** Edit 7434 to NIUSR.MAC by PRATT on 2-Apr-87, for SPR #21423
; Change several uses of the extended code PSECT macros to be correct. In
; particular, fix OPOPACs and ILLUUOs occurring out of NIURC1.
; *** Edit 7285 to NIUSR.MAC by MCCOLLUM on 14-Apr-86
; Return global job number in INTOEX
; Edit 7173 to NIUSR.MAC by PALMIERI on 23-Oct-85 (TCO 6.1.1542)
; Move modules NIUSR and LLMOP to an extended section. This required the
; changing of some global routine names in LLMOP; Therefor the changes to
; MEXEC, JSYSA, and FORK.
; UPD ID= 2137, SNARK:<6.1.MONITOR>NIUSR.MAC.28, 5-Jun-85 10:07:19 by MCCOLLUM
;TCO 6.1.1406 - Update copyright notice.
; UPD ID= 2031, SNARK:<6.1.MONITOR>NIUSR.MAC.27, 28-May-85 17:35:36 by GROSSMAN
;TCO 6.1.1415 - Fix up multicast returns in .EIRPI, and fix up handling of
;global portal IDs.
; UPD ID= 1903, SNARK:<6.1.MONITOR>NIUSR.MAC.26, 4-May-85 20:13:00 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1821, SNARK:<6.1.MONITOR>NIUSR.MAC.25, 24-Apr-85 19:57:38 by MCCOLLUM
;TCO 6.1.1238 - Fix BUG. documentation
; UPD ID= 1805, SNARK:<6.1.MONITOR>NIUSR.MAC.24, 24-Apr-85 13:06:06 by GROSSMAN
; TCO 6.1.1341 - Add ERJMPs after all PXCTs that occur in NOINT code.
; UPD ID= 1802, SNARK:<6.1.MONITOR>NIUSR.MAC.23, 23-Apr-85 17:41:39 by GROSSMAN
; TCO 6.1.1337 - Move SETNIA from NISRV to here.
; UPD ID= 1800, SNARK:<6.1.MONITOR>NIUSR.MAC.22, 23-Apr-85 17:19:24 by GROSSMAN
; TCO 6.1.1336 - Return Physical and Hardware addresses for .EIRCI function.
; UPD ID= 1657, SNARK:<6.1.MONITOR>NIUSR.MAC.21, 20-Mar-85 09:41:10 by GROSSMAN
; TCO 6.1.1278 - Call GL2LCL in FNDGPR.
; UPD ID= 1652, SNARK:<6.1.MONITOR>NIUSR.MAC.20, 18-Mar-85 16:54:26 by GROSSMAN
; Implement Read Channel List and Read Portal List
; UPD ID= 1535, SNARK:<6.1.MONITOR>NIUSR.MAC.19, 19-Feb-85 13:52:15 by GROSSMAN
; TCO 6.1.1210 - Implement Read Portal Counters.
; UPD ID= 1528, SNARK:<6.1.MONITOR>NIUSR.MAC.18, 18-Feb-85 22:52:28 by GROSSMAN
; TCO 6.1.1203 - Fix code reading bug in blocking xmits.
; UPD ID= 1527, SNARK:<6.1.MONITOR>NIUSR.MAC.17, 18-Feb-85 22:19:19 by GROSSMAN
; TCO 6.1.1202 - Implement Read Channel Counters.
; UPD ID= 1414, SNARK:<6.1.MONITOR>NIUSR.MAC.16, 28-Jan-85 14:42:30 by GROSSMAN
; More TCO 6.1.1157 - Make NIJJIF global.
; UPD ID= 1411, SNARK:<6.1.MONITOR>NIUSR.MAC.15, 28-Jan-85 10:25:54 by GROSSMAN
; TCO 6.1.1157 - Make NIJJIF use NIJFLG instead of NIJJSR.
; UPD ID= 1409, SNARK:<6.1.MONITOR>NIUSR.MAC.14, 28-Jan-85 10:13:11 by GROSSMAN
; Make GXRCOR, RXRCOR, RELCSH, DQBRB and RQBRB swappable.
; UPD ID= 1399, SNARK:<6.1.MONITOR>NIUSR.MAC.13, 23-Jan-85 17:12:27 by GLINDELL
; Fix PSI handling in NIJJIF
; 1) calculate channel number correctly
; 2) load fork number before calling PSIRQB
; UPD ID= 1370, SNARK:<6.1.MONITOR>NIUSR.MAC.12, 21-Jan-85 11:54:13 by GLINDELL
; 1) Made the following routines resident:
; .NION, CLOCBK, GXRCOR, RXRCOR, RELCSH, DQBRB and friends
; 2) Changed JSR BUGHLT to BUG. at NIUCL1
; UPD ID= 1365, SNARK:<6.1.MONITOR>NIUSR.MAC.11, 19-Jan-85 16:19:11 by PAETZOLD
;TCO 6.1.1146 - Make .NIOFF resident also. NIJJIF calls it.
; UPD ID= 1359, SNARK:<6.1.MONITOR>NIUSR.MAC.10, 17-Jan-85 16:35:53 by GROSSMAN
; More TCO 6.1.1140 - Make NIJJIF resident.
; UPD ID= 1354, SNARK:<6.1.MONITOR>NIUSR.MAC.9, 16-Jan-85 17:11:27 by GROSSMAN
; TCO 6.1.1141 - Add transmit and receive memory cache (GXRCOR).
; TCO 6.1.1140 - Add (ugh, bletch!) NIJJIF.
; TCO 6.1.1138 - Add .EIGET and .EIREL.
; UPD ID= 1189, SNARK:<6.1.MONITOR>NIUSR.MAC.8, 11-Dec-84 17:29:19 by GROSSMAN
; More TCO 6.1.1082 - Don't add DLLERB to successful returns.
; UPD ID= 1188, SNARK:<6.1.MONITOR>NIUSR.MAC.7, 11-Dec-84 15:20:27 by GROSSMAN
; TCO 6.1.1082 -
; Return status in BXSTA upon completion of transmits and receives. Allow
; blocking mode transmits and receives to be interrupted. Ensure that receive
; buffers are on a writeable page by writing a byte into the buffer before
; using it.
; UPD ID= 1136, SNARK:<6.1.MONITOR>NIUSR.MAC.6, 30-Nov-84 17:26:03 by GROSSMAN
; Lots of development. Add ENABLE/DISABLE multicast. Do quota checking on
; xmit and rcv buffers. Implement blocking mode xmits and rcvs.
; UPD ID= 1081, SNARK:<6.1.MONITOR>NIUSR.MAC.5, 15-Nov-84 14:28:10 by GROSSMAN
; TCO 6.1.1054 - Change symbols to avoid conflict with other monitor modules.
; TCO 6.1.1045 - Create this module.
; COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1976, 1988.
; ALL RIGHTS RESERVED.
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
; ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE
; INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER
; COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
; OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY
; TRANSFERRED.
;
; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
; AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
; CORPORATION.
;
; DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
; SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
;Ethernet user mode interface (NI% JSYS). - Stu Grossman
SEARCH PROLOG,NIPAR,MONSYM
TTITLE NIUSR
XSWAPCD ; [7173] Make this be swappable
Subttl Table of Contents
; Table of Contents for NIUSR
;
; Section Page
;
;
; 1. Definitions . . . . . . . . . . . . . . . . . . . . . 3
; 2. Storage . . . . . . . . . . . . . . . . . . . . . . . 4
; 3. NI% JSYS . . . . . . . . . . . . . . . . . . . . . . . 5
; 4. .EIOPN - Open a Portal . . . . . . . . . . . . . . . . 6
; 5. .EICLO - Close a portal . . . . . . . . . . . . . . . 7
; 6. CLOCBK - Callback from NISRV for close complete . . . 8
; 7. .EIXMT - Transmit Datagrams . . . . . . . . . . . . . 9
; 8. NIURTQ - Read Transmit Queue . . . . . . . . . . . . . 11
; 9. XMTCBK - Transmit completion callback from NISRV . . . 12
; 10. NIJJIF - NI% JSYS Jiffy Service . . . . . . . . . . . 13
; 11. .EIRCV - Post Receive buffers . . . . . . . . . . . . 14
; 12. .EIRRQ - Read Receive Queue . . . . . . . . . . . . . 16
; 13. RCVCBK - Receive completion callback from NISRV . . . 17
; 14. .EIEMA & .EIDMA - Enable/Disable a multicast address . 18
; 15. LOKBUF - Lock down user buffers . . . . . . . . . . . 19
; 16. ULKBUF - Unlock buffers . . . . . . . . . . . . . . . 20
; 17. DOINT - Process pending PSI's . . . . . . . . . . . . 21
; 18. USRCBK - Callback routine from NISRV . . . . . . . . . 22
; 19. GETBRB & RETBRB - Get and Return Buffer Request blocks 23
; 20. QUEBRB - Queue up BRB's . . . . . . . . . . . . . . . 24
; 21. SETTRB - Set EITBA and EIRBA as appropriate . . . . . 25
; 22. CREPOR - Create a Portal Block . . . . . . . . . . . . 26
; 23. KILPOR - Release all resources associated with a porta 27
; 24. NIJKFK - Reset for the NI% JSYS . . . . . . . . . . . 28
; 25. ***** Hacks ***** . . . . . . . . . . . . . . . . . . 29
; 26. .EIRCI - Read Channel Information . . . . . . . . . . 30
; 27. .EISCA - Set Channel Address . . . . . . . . . . . . . 31
; 28. .EISCS - Set Channel State . . . . . . . . . . . . . . 32
; 29. .EIRCC & .EIRPC - Read Channel & Portal Counters . . . 33
; 30. .EIGET and .EIREL - Get and release a KLNI . . . . . . 34
; 31. .EIRCL - Read Channel List . . . . . . . . . . . . . . 35
; 32. .EIRPL - Read Portal List . . . . . . . . . . . . . . 36
; 33. INTOEX - Convert internal portal ID's to external port 37
; 34. .EIRPI - Read Portal Info . . . . . . . . . . . . . . 38
; 35. CHKOWN - Check ownership of KLNI . . . . . . . . . . . 39
; 36. FNDPOR - Find the portal database associated with a po 40
; 37. FNDGPR - Find NISRV portal IDs . . . . . . . . . . . . 41
; 38. SMON% JSYS - Set NI address . . . . . . . . . . . . . 42
; 39. End of NIUSR . . . . . . . . . . . . . . . . . . . . . 43
SUBTTL Definitions
EXTERN UPLIST
DEFAC UN,Q1 ; Define UN block pointer
DEFAC PR,Q2 ; Define portal block pointer
MAXPOR==^D16 ; Maximum number of portals
; The portal list structure
BEGSTR PL
HWORD MAX ; Highest portal defined so far
HWORD NUM ; Total number of portals defined so far
WORD LIS,MAXPOR ; The actual portal list
ENDSTR
; Queue headers (used for xmit and rcv queues)
BEGSTR XR
WORD QUH ; Pointer to first item
WORD QUE ; Pointer to last item
ENDSTR
; Portal block
BEGSTR PR
WORD NXT ; Pointer to next portal
FIELD UPD,6 ; User's portal ID
FIELD FLG,6 ; Flags for this portal
BIT CCP ; Close complete
BIT RPS ; Receive PSI requested
BIT TPS ; Transmit PSI requested
BIT SPS ; Status change PSI requested
FIELD LFK,6 ; Job wide fork #
FIELD FRK,18 ; Fork number of owning fork
WORD CHK ; Check word
FIELD TCH,12 ; Transmit completion interrupt channel
FIELD RCH,12 ; Receive completion interrupt channel
FIELD SCH,12 ; Status change interrupt channel
WORD UNB ; UN block pointer
WORD PID ; Monitor's portal ID
WORD XQH,XR.LEN ; Transmitted queue header
WORD RQH,XR.LEN ; Receive queue header
WORD TRQ ; Transmit quota
WORD RCQ ; Receive quota
WORD TIP ; # of transmit buffers queued up to NISRV
WORD RIP ; # of receive buffers queued up to NISRV
ENDSTR
; Internal buffer descriptor block
BEGSTR BR
WORD NXT ; Pointer to next BR block
HWORD BSZ ; Buffer size
HWORD PRO ; Protocol type
HWORD CP1 ; Locked core page 1
HWORD CP2 ; Locked core page 2
WORD BFA,2 ; Buffer address (Byte pointer)
WORD BID ; Buffer ID
WORD STA ; Return status
WORD DAD,2 ; Destination Ethernet address
WORD SAD,2 ; Source Ethernet address
ENDSTR
BEGSTR PI ; Structure for global portal IDs
FILLER 18 ; Use only low order bits
FIELD FRK,9 ; Job wide fork number
FIELD PID,9 ; Job wide portal ID
ENDSTR
FC.POR==1B1 ; Function requires a portal
DLLERB==NIENSP-UNNSP% ; Conversion factor for NISRV to normal
; error codes
SUBTTL Storage
RSI (UPRBAS,<0>) ; Portal list base address
RSI (KNIOWN,<-1>) ; Contains fork # of owning fork
RSI (NIJFLG,<0>) ; Non-0 means scheduler should call NIJJIF
SUBTTL NI% JSYS
XNENT (.NI,G) ; [7434] .NI::, and X.NI::
MCENT ; Enter JSYS context
SAVEAC <P1,UN,PR>
; Here after taking a PSI
NI0: MOVX T1,SC%WHL!SC%OPR ; Wheel or operator
TDNN T1,CAPENB ; capabilities enabled?
ITERR (CAPX1) ; Nope, die horribly
UMOVE P1,T1 ; Get pointer to user's argument block
ULOAD T3,EIFCN,(P1) ; Get the function code
SKIPLE T3 ; Jump if illegal function code
CAXLE T3,.EIMAX ; Is the function in range?
ITERR (ARGX02) ; Nope, complain
MOVE T3,FUNTAB-1(T3) ; Get the table entry
TXNN T3,FC.POR ; Does he need a Portal ID?
JRST (T3) ; Nope, just dispatch
NOINT ; No interrupts please
CALL FNDPOR ; Look for the portal block
ITERR ; Pass the error upwards
MOVE PR,T1 ; Setup the official pointer
LOAD UN,PRUNB,(PR) ; Get our UN block pointer
JRST (T3) ; Call the processor
; Dispatch and bits for function codes
DEFINE XX (code,flags<0>)<TABENT .EI'code,<IFIW!flags!<NIU'code&777777>>> ;[7173]
FUNTAB: TABBEG 1,.EIMAX,<FOO>
XX OPN ; Open a Portal
XX CLO,FC.POR ; Close a Portal
XX RCV,FC.POR ; Post a Receive Buffer
XX RRQ,FC.POR ; Read Receive Queue
XX XMT,FC.POR ; Transmit a Datagram
XX RTQ,FC.POR ; Read Transmit Queue
XX EMA,FC.POR ; Enable a Multicast
XX DMA,FC.POR ; Disable a Multicast
XX RPL ; Read Portal List
XX RCL ; Read Channel List
XX RPC ; Read Portal Counters
XX RCC ; Read Channel Counters
XX RPI ; Read Portal Information
XX RCI ; Read Channel Information
XX SCS ; Set Channel State
XX SCA ; Set Channel Address
XX GET ; Obtain ownership of the channel
XX REL ; Release ownership of the channel
TABEND
SUBTTL .EIOPN - Open a Portal
NIUOPN: NOINT ; No PSI's please
CALL CREPOR ; Create a portal, setup PR and UN
ITERR ; Couldn't get a portal block
; Note well! We are now NOINT.
ULOAD T1,EICHN,(P1) ; Get the channel number
ERJMP OPNER1 ; Pass the error upwards
STOR T1,UNCHN,(UN) ; Save channel number in the UN block
ULOAD T1,EIPAD,(P1) ; Get pad flag
ERJMP OPNER1 ; Pass the error upwards
STOR T1,UNPAD,(UN) ; Save it in the UN block
ULOAD T1,EIPRO,(P1) ; Get the protocol type
ERJMP OPNER1 ; Pass the error upwards
STOR T1,UNPRO,(UN) ; Save the protocol type in the UN block
ULOAD T1,EITCH,(P1) ; Get transmit completion PSI channel
ERJMP OPNER1 ; Pass the error upwards
STOR T1,PRTCH,(PR) ; Save it in the portal block
ULOAD T1,EIRCH,(P1) ; Get receive completion PSI channel
ERJMP OPNER1 ; Pass the error upwards
STOR T1,PRRCH,(PR) ; Save it in the portal block
ULOAD T1,EISCH,(P1) ; Get status change PSI channel
ERJMP OPNER1 ; Pass the error upwards
STOR T1,PRSCH,(PR) ; Save it in the portal block
LOAD T1,PRUPD,(PR) ; Get the user's portal ID
USTOR T1,EIPID,(P1) ; Return it to the user
ERJMP OPNER1 ; Pass the error upwards
MOVE T1,FORKX ; Get our system wide fork index
STOR T1,PRFRK,(PR) ; This fork now owns this portal
MOVE T1,FORKN ; Get our job wide fork #
STOR T1,PRLFK,(PR) ; Setup for information gatherers
OPSTR <XMOVEI T1,>,PRXQH,+XR.QUH(PR) ; Get ptr to xmit q header
STOR T1,PRXQH,+XR.QUE(PR) ; Make xmt q end point to xmit q hdr
OPSTR <XMOVEI T1,>,PRRQH,+XR.QUH(PR) ; Get ptr to rcv q header
STOR T1,PRRQH,+XR.QUE(PR) ; Make rcv q end point to rcv q hdr
MOVX T1,8 ; Transmit and receive quota
STOR T1,PRTRQ,(PR) ; Setup transmit quota
STOR T1,PRRCQ,(PR) ; Setup receive quota
STOR PR,PRCHK,(PR) ; Setup the check word
STOR PR,UNUID,(UN) ; Install the callback ID for this portal
XMOVEI T1,USRCBK ; Get the address of the callback routine
STOR T1,UNCBA,(UN) ; Save it in the UN block
MOVX T1,NU.OPN ; Function is Open a Portal
MOVE T2,UN ; Get UN block pointer
CALL DLLUNI ; Open the portal
JRST OPNERR ; Go process the error
LOAD T1,UNPID,(UN) ; Get the Portal ID that NISRV gave us
STOR T1,PRPID,(PR) ; Save it for later reference
MOVX T1,UNA.UV ; Indicate that all buffers are user mode
STOR T1,UNADS,(UN) ; Save it in the UN block
OKINT ; Allow PSI's
MRETNG ; All done, return success
; Here when LSTERR contains last error
OPNER1: MOVE T1,LSTERR ; Retreive last error
SUBI T1,DLLERB ; Correct for later addition
; Here with error code in T1
OPNERR: SAVEAC P2
MOVE P2,T1 ; Save the error code
CALL KILPOR ; Kill the portal block
OKINT ; Allow PSI's
MOVE T1,P2 ; Retreive the error code
ADDI T1,DLLERB ; Convert it to a real error code
ITERR ; And return the error to the user
SUBTTL .EICLO - Close a portal
NIUCLO: CALL NIUCLI ; Here from JSYS context
OKINT ; Ok to interrupt now
MRETNG
; Here for internal calls to the close function
NIUCLI: MOVX T1,NU.CLO ; Get function code
MOVE T2,UN ; Get UN block pointer
CALL DLLUNI ; Tell NISRV to close the portal
JRST NIUCL1 ; Probably a resource error, try again
NIUCL2: MOVE T1,FORKX ; Get our fork #
STOR PR,FKST2,(T1) ; Setup portal pointer for scheduler test
MOVX T1,CLOTST ; Get routine address
MDISMS ; Wait until close has been done
OPSTR <XMOVEI T1,>,PRXQH,(PR) ; Get xmit queue header
CALL FREBRL ; Release the entire transmit queue
OPSTR <XMOVEI T1,>,PRRQH,(PR) ; Get rcv queue header
CALL FREBRL ; Release the entire receive queue
CALL RELCSH ; Flush the memory cache
TRN ; Who cares..
CALLRET KILPOR ; Murder the PR block and such
; Scheduler test to see if anything has closed
RESCD
FX=:7
CLOTST: LOAD T1,FKST2,(FX) ; Get portal block pointer
JE PRCCP,(T1),RTN ; Non-skip if not closed
RETSKP ; Give skip return, portal is closed
XSWAPCD ; [7173]
; Here on failure from NISRV close function
NIUCL1: CAIE T1,UNRES% ; Resource error?
BUG. (HLT,NIJECL,NIUSR,SOFT,<Error closing portal>,<<T1,ERROR>>,<
Cause: NISRV returned an error when we tried to close a portal. The error
code was not UNRES% (Resource error), which is the only one that may
occur.
Data: ERROR - The returned error code
>)
CALL RELCSH ; Try to shake a little loose
TRNA ; No memory in cache, sleep for a while
JRST NIUCLI ; Try the close again
MOVX T1,^D500 ; Try again in 1/2 a second
DISMS% ; Wait for things to calm down
JRST NIUCLI ; Try again
; Here to free all BR blocks on a xmit or rcv queue
FREBRL: SAVEAC <P1,P3>
MOVE P1,T1 ; Put q header into a safe place
FREBR1: MOVE T1,P1 ; Get q header
CALL DQBRB ; Get the next block
JRST RTN ; All done, return
MOVE T1,P3 ; Get block address
CALL RXRCOR ; Release the BR block
JRST FREBR1 ; Loop till done
SUBTTL CLOCBK - Callback from NISRV for close complete
XRESCD ; [7173]
CLOCBK: JUMPE PR,RTN ; Jump if just temp user
SETONE PRCCP,(PR) ; Indicate that portal is now closed
RET
XSWAPCD ; [7173]
SUBTTL .EIXMT - Transmit Datagrams
NIUXMT: SAVEAC <P2,P3>
SETZ P3, ; Clear BR pointer in case of error
ULOAD P2,EIBCP,(P1) ; Get pointer to buffer descriptor chain
ERJMP [ITERX] ; Pass error upwards
XMTLOP: JUMPE P2,XMTDON ; End of chain, we're all done
CALL GETBRT ; Get a buffer descriptor block
ITERR ; Pass error upwards
MOVE P3,T1 ; Put BR block pointer in a safe place
ULOAD T1,BXBSZ,(P2) ; Get length of datagram
ERJMP XMTER1
STOR T1,UNBSZ,(UN) ; Put it in the UN block
STOR T1,BRBSZ,(P3) ; Put it in the BR block
XCTU [OPSTR <DMOVE T1,>,BXBFA,(P2)] ; Get byte pointer
ERJMP XMTER1
OPSTR <DMOVEM T1,>,UNBFA,(UN) ; Put it in the UN block
OPSTR <DMOVEM T1,>,BRBFA,(P3) ; Put it in the BR block
XCTU [OPSTR <DMOVE T1,>,BXDAD,(P2)] ; Get destination address
ERJMP XMTER1
OPSTR <DMOVEM T1,>,UNDAD,(UN) ; Put it in the UN block
OPSTR <DMOVEM T1,>,BRDAD,(P3) ; Put it in the BR block
ULOAD T1,BXBID,(P2) ; Get buffer ID
ERJMP XMTER1
STOR T1,BRBID,(P3) ; Save it for later
STOR P3,UNRID,(UN) ; Make request ID word point to BR block
CALL LOKBUF ; Lock up the buffer
MOVX T1,NU.XMT ; Tell NISRV to transmit
MOVE T2,UN ; Get address of UN block
CALL DLLUNI ; Transmit the buffer
JRST XMTERR ; Deal with the error
OPSTR AOS,PRTIP,(PR) ; Increment transmits in progress
ULOAD P2,BXNXT,(P2) ; Get pointer to next BXB
ERJMP [ITERX] ; Pass error upwards
JRST XMTLOP ; And process next buffer
; Here when we reach the end of the BXB chain
XMTDON: ULOAD T1,EIBLK,(P1) ; Get the block bit
ERJMP [ITERX] ; Pass the error upwards
JUMPN T1,XMTBLK ; Jump if he wants to wait
OKINT ; Allow interrupts
MRETNG ; All done!
; Here when an error occurs and BR block needs to be released, but buffer
; is not yet locked.
XMTER1: SAVEAC P1
MOVE T1,LSTERR ; Get last error code
SUBI T1,DLLERB ; Correct for later addition
MOVE P1,T1 ; Get error code into correct AC
JRST XMTER2 ; Release BR block, but don't unlock buffer
; Here when we get an error from NISRV
XMTERR: SAVEAC P1
MOVE P1,T1 ; Save the NISRV error code
CALL ULKBUF ; Unlock the buffer
XMTER2: MOVE T1,P3 ; Get pointer to BR block
CALL RELBRT ; Release the BR block
USTOR P2,EIBCP,(P1) ; Point at the offending buffer
ERJMP [ITERX] ; I really hate errors within errors...
OKINT ; We can allow interrupts now
ADDI P1,DLLERB ; Convert NISRV error to a real error code
MOVE T1,P1 ; Get the error code into the right place
ITERR ; Pass the error on to the user
; Here if user wants JSYS to block until all buffers are transmitted
XMTBLK: MOVE T1,FORKX ; Get our fork number
STOR PR,FKST2,(T1) ; Setup our portal address for sched test
MOVEI T1,XMTTST ; Get address of scheduler test
MDISMS ; Dismiss till buffers come back
MOVE T1,FORKX ; Get our fork #
MOVE T1,FKINT(T1) ; Get the interrupt bits
TXNE T1,FKPS1 ; Interrupt pending?
CALL INTXMT ; Yes, go process it
JN PRTIP,(PR),XMTBLK ; Wait some more if buffers didn't get xmitted
JRST NIURTQ ; All done, return the buffers
RESCD ; Scheduler tests must be resident
XMTTST: JN FKPS1,(FX),RSKP ; Wakeup if a PSI is pending
LOAD T1,FKST2,(FX) ; Get pointer to portal block
JE PRTIP,(T1),RSKP ; Wake up job if all buffers got xmitted
RET ; Sleep some more
XSWAPCD ; [7173]
; Here when fork woke up because of a pending PSI
INTXMT: OKINT ; Allow the PSI to happen
NOINT ; Clam up again
; CALL CHKARG ; Check the arguments again
RET
SUBTTL NIURTQ - Read Transmit Queue
NIURTQ: SAVEAC <P2,P3>
ULOAD P2,EIBCP,(P1) ; Get pointer to buffer descriptor chain
ERJMP [ITERX] ; Pass the error upwards
RTQLOP: OPSTR <XMOVEI T1,>,PRXQH,(PR) ; Get address of transmit q header
CALL DQBRB ; Dequeue the next buffer request block
JRST RTQDON ; No more buffers, quit now
JUMPE P2,RTQEOL ; Not enough BXBs, quit now
LOAD T1,BRBSZ,(P3) ; Get datagram size
USTOR T1,BXBSZ,(P2) ; Put it into the user's block
ERJMP RTQERR
OPSTR <DMOVE T1,>,BRBFA,(P3) ; Get the byte pointer
XCTU [OPSTR <DMOVEM T1,>,BXBFA,(P2)] ; Put BP into user's block
ERJMP RTQERR
OPSTR <DMOVE T1,>,BRDAD,(P3) ; Get Destination address
XCTU [OPSTR <DMOVEM T1,>,BXDAD,(P2)] ; Put dest addr into user's block
ERJMP RTQERR
LOAD T1,BRBID,(P3) ; Get user's buffer ID
USTOR T1,BXBID,(P2) ; Put buffer ID into user's block
ERJMP RTQERR
LOAD T1,BRSTA,(P3) ; Get the return status
SKIPE T1 ; Don't add error offset if successful
ADDI T1,DLLERB ; Convert this to a user error code
USTOR T1,BXSTA,(P2) ; Put it where the user can find it
ERJMP RTQERR
MOVX T1,1 ; Get a bit
USTOR T1,BXVAL,(P2) ; Set the valid bit in user's buffer descriptor
ERJMP RTQERR
MOVE T1,P3 ; Get pointer to BR block
CALL RELBRT ; Release the BR block
TSTINT ; Any interrupts pending?
JRST DOINT ; Yes, go process them
ULOAD P2,BXNXT,(P2) ; Get pointer to next buffer descriptor
ERJMP [ITERX]
JRST RTQLOP ; No, process next buffer
; Here on an error when the BR block needs to be requeued
RTQERR: OPSTR <XMOVEI T1,>,PRXQH,(PR) ; Get address of transmit q header
CALL RQBRB ; Requeue the BRB (onto head of xmit queue)
CALL SETTRB ; Set the TBA and RBA bits as appropriate
ITERX ; Pass error upwards
; Here when the transmit queue is empty
RTQDON: CALL SETTRB ; Go set the TBA and RBA bits
OKINT ; Re-enable interrupts
SETZ T1, ; Get a 0
SKIPE P2 ; Don't clear bit if no block
USTOR T1,BXVAL,(P2) ; Clear the valid bit in the last block
MRETNG ; And return success
; Here when user didn't supply enough buffer descriptors
RTQEOL: OPSTR <XMOVEI T1,>,PRXQH,(PR) ; Get address of transmit q header
CALL RQBRB ; Requeue the BRB (onto head of xmit queue)
CALL SETTRB ; Go set the TBA and RBA bits
OKINT
MRETNG
SUBTTL XMTCBK - Transmit completion callback from NISRV
XRESCD ; [7173]
XMTCBK: SAVEAC P3
LOAD P3,UNRID,(UN) ; Setup BR block pointer
STOR T3,BRSTA,(P3) ; Store the return status
OPSTR <XMOVEI T1,>,PRXQH,(PR) ; Get transmit queue header
CALL QUEBRB ; Put this one on the transmit queue
CALL XMTPSI ; Queue went non-empty, give user a PSI
OPSTR SOS,PRTIP,(PR) ; Decrement transmits in progress
CALLRET ULKBUF ; Unlock the buffer
RCVPSI: LOAD T1,PRRCH,(PR) ; Get the receive completion PSI channel
CAIL T1,^D36 ; Channel number in range?
RET ; No, don't give a PSI
SETONE PRRPS,(PR) ; Request an interrupt from jiffy level
SETOM NIJFLG ; Request jiffy service
RET
XMTPSI: LOAD T1,PRTCH,(PR) ; Get the transmit completion PSI channel
CAIL T1,^D36 ; Channel number in range?
RET ; No, don't give a PSI
SETONE PRTPS,(PR) ; Request an interrupt from jiffy level
SETOM NIJFLG ; Request jiffy service
RET
SUBTTL NIJJIF - NI% JSYS Jiffy Service
XRENT (NIJJIF,G) ; [7434] NIJJIF:: and XNIJJI:: Run in sched
SAVEAC PR
SETZM NIJFLG ; Clear flag that got us here
CALL .NIOFF ; No interrupts please
XMOVEI PR,UPRBAS ; Get the head of the chain
NIJJI1: LOAD PR,PRNXT,(PR) ; Get the next portal
JUMPE PR,.NION ; Return if all done
SETZ T1, ; Clear the bit mask
JE PRRPS,(PR),NIJJI2 ; Jump if no receive PSI requested
SETZRO PRRPS,(PR) ; Clear the request
LOAD T2,PRRCH,(PR) ; Get the receive PSI channel
OR T1,BITS(T2) ; And or in the corresponding bit
NIJJI2: JE PRTPS,(PR),NIJJI3 ; Jump if no transmit PSI requested
SETZRO PRTPS,(PR) ; Clear the request
LOAD T2,PRTCH,(PR) ; Get the transmit PSI channel
OR T1,BITS(T2) ; And or in the corresponding bit
NIJJI3: JE PRSPS,(PR),NIJJI4 ; Jump if no status change PSI requested
SETZRO PRSPS,(PR) ; Clear the request
LOAD T2,PRSCH,(PR) ; Get the status change PSI channel
OR T1,BITS(T2) ; And or in the corresponding bit
NIJJI4: JUMPE T1,NIJJI1 ; Jump if no PSI's to be issued
LOAD T2,PRFRK,(PR) ; Get fork number of fork to wake up
XCALL (MSEC1,PSIRQB) ; [7173] Request some interrupts
JRST NIJJI1 ; And loop back for more
XSWAPCD ; [7173] Make us swappable again
SUBTTL .EIRCV - Post Receive buffers
NIURCV: SAVEAC <P2,P3>
SETZ P3, ; Clear BR pointer in case of error
ULOAD P2,EIBCP,(P1) ; Get pointer to buffer descriptor chain
ERJMP [ITERX]
RCVLOP: JUMPE P2,RCVDON ; Nope, we're all done
CALL GETBRR ; Get a buffer descriptor block
ITERR ; Pass error upwards
MOVE P3,T1 ; Put BR block pointer in a safe place
XCTU [OPSTR <DMOVE T1,>,BXBFA,(P2)] ; Get byte pointer
ERJMPR RCVER1
OPSTR <DMOVEM T1,>,UNBFA,(UN) ; Put it in the UN block
OPSTR <DMOVEM T1,>,BRBFA,(P3) ; Put it in the BR block
; Note well! In order to prevent "spurious" user ?Ill mem refs, the pages
; that the buffer occupies must be private and writeable. We ensure this
; by writing something in the first and last byte of the buffer prior to
; locking down the pages that the buffer occupies.
XCTBU [IDPB T1,T1] ; Write the first byte
ERJMPR RCVER1
ULOAD T1,BXBSZ,(P2) ; Get length of the buffer
ERJMPR RCVER1
STOR T1,UNBSZ,(UN) ; Put it in the UN block
STOR T1,BRBSZ,(P3) ; Put it in the BR block
SUBI T1,1 ; Back off by a byte
OPSTR <ADJBP T1,>,BRBFA,(P3) ; Point to the last legal byte
XCTBU [IDPB T1,T1] ; Write into the last byte
ERJMPR RCVER1
ULOAD T1,BXBID,(P2) ; Get buffer ID
ERJMPR RCVER1
STOR T1,BRBID,(P3) ; Save it for later
STOR P3,UNRID,(UN) ; Make request ID word point to BR block
CALL LOKBUF ; Lock up the buffer
MOVX T1,NU.RCV ; Tell NISRV to post this buffer
MOVE T2,UN ; Get address of UN block
CALL DLLUNI ; Post the buffer
JRST RCVERR ; Deal with the error
OPSTR AOS,PRRIP,(PR) ; Increment receives in progress
TSTINT ; Any interrupts pending?
JRST DOINT ; Yes, process them
ULOAD P2,BXNXT,(P2) ; Get pointer to next BXB
ERJMP [ITERX]
JRST RCVLOP ; And process next buffer
; Here when we reach the end of the BXB chain
RCVDON: ULOAD T1,EIBLK,(P1) ; Get the block bit
ERJMP [ITERX]
JUMPN T1,RCVBLK ; Jump if user wants to block
OKINT ; Allow interrupts
USTOR P2,EIBCP,(P1) ; Point to the last buffer processed
MRETNG ; All done!
; Here when we get an error from NISRV
RCVERR: ADDI T1,DLLERB ; Convert NISRV error to a real error code
; Here on other types of errors
RCVER1: SAVEAC P1
MOVE P1,T1 ; Save the NISRV error code
CALL ULKBUF ; Unlock the buffer
MOVE T1,P3 ; Get pointer to BR block
CALL RELBRR ; Release the BR block
OKINT ; We can allow interrupts now
USTOR P2,EIBCP,(P1) ; Point at the offending buffer
MOVE T1,P1 ; Get the error code into the right place
ITERR ; Pass the error on to the user
; Here when user wants to block until at least one buffer has received a
; datagram.
RCVBLK: MOVE T1,FORKX ; Get our fork number
STOR PR,FKST2,(T1) ; Setup our portal address for sched test
MOVEI T1,RCVTST ; Get address of scheduler test
MDISMS ; Dismiss till a buffer comes back
MOVE T1,FORKX ; Get our fork #
MOVE T1,FKINT(T1) ; Get the interrupt bits
TXNE T1,FKPS1 ; Interrupt pending?
CALL INTRCV ; Yes, go process it
OPSTR SKIPN,XRQUH,+$PRRQH(PR) ; Any buffers yet?
JRST RCVBLK ; Wait some more if no buffers yet
JRST NIURRQ ; All done, return the buffers
RESCD ; Scheduler tests must be resident
RCVTST: JN FKPS1,(FX),RSKP ; Wakeup if a PSI is pending
LOAD T1,FKST2,(FX) ; Get the PR block address
OPSTR SKIPN,XRQUH,+$PRRQH(T1) ; Any buffers yet?
RET ; No, sleep some more
RETSKP ; Yes, wakeup the job
XSWAPCD ; [7173]
INTRCV: OKINT
NOINT
RET
SUBTTL .EIRRQ - Read Receive Queue
NIURRQ: SAVEAC <P2,P3>
ULOAD P2,EIBCP,(P1) ; Get pointer to buffer descriptor chain
ERJMP [ITERX] ; Pass the error upwards
RRQLOP: OPSTR <XMOVEI T1,>,PRRQH,(PR) ; Get address of receive q header
CALL DQBRB ; Dequeue the next buffer request block
JRST RRQDON ; All done quit now
JUMPE P2,RRQEOL ; All done, exit
OPSTR <DMOVE T1,>,BRSAD,(P3) ; Get source Ethernet address
XCTU [OPSTR <DMOVEM T1,>,BXSAD,(P2)] ; Put it in the user's block
ERJMP RRQERR
OPSTR <DMOVE T1,>,BRDAD,(P3) ; Get destination Ethernet address
XCTU [OPSTR <DMOVEM T1,>,BXDAD,(P2)] ; Put dest addr into user's block
ERJMP RRQERR
LOAD T1,BRPRO,(P3) ; Get protocol type
USTOR T1,BXPRO,(P2) ; Put it into the user's buffer descriptor
ERJMP RRQERR
LOAD T1,BRBSZ,(P3) ; Get datagram size
USTOR T1,BXBSZ,(P2) ; Put it into the user's block
ERJMP RRQERR
LOAD T1,BRSTA,(P3) ; Get the return status
SKIPE T1 ; Don't add error offset if successful
ADDI T1,DLLERB ; Convert this to a user error code
USTOR T1,BXSTA,(P2) ; Put it where the user can find it
ERJMP RRQERR
OPSTR <DMOVE T1,>,BRBFA,(P3) ; Get the byte pointer
XCTU [OPSTR <DMOVEM T1,>,BXBFA,(P2)] ; Put BP into user's block
ERJMP RRQERR
LOAD T1,BRBID,(P3) ; Get user's buffer ID
USTOR T1,BXBID,(P2) ; Put buffer ID into user's block
ERJMP RRQERR
MOVX T1,1 ; Get a bit
USTOR T1,BXVAL,(P2) ; Set the valid bit in user's buffer descriptor
ERJMP RRQERR
MOVE T1,P3 ; Get pointer to BR block
CALL RELBRR ; Release the BR block
TSTINT ; Any interrupts pending?
JRST DOINT ; Yes, go process them
ULOAD P2,BXNXT,(P2) ; Get pointer to next buffer descriptor
ERJMP [ITERX]
JRST RRQLOP ; No, process next buffer
; Here when the receive queue is empty
RRQDON: CALL SETTRB ; Go set the TBA and RBA bits
OKINT ; Re-enable interrupts
SETZ T1, ; Get a 0
SKIPE P2 ; Don't clear bit if no block
USTOR T1,BXVAL,(P2) ; Clear the valid bit in the last block
MRETNG ; And return success
; Here when user didn't supply enough buffer descriptors
RRQEOL: OPSTR <XMOVEI T1,>,PRRQH,(PR) ; Get address of receive q header
CALL RQBRB ; Requeue the BRB (onto head of rcv queue)
CALL SETTRB ; Go set the TBA and RBA bits
OKINT
MRETNG
; Here when we get an error, and have to requeue the BRB
RRQERR: OPSTR <XMOVEI T1,>,PRRQH,(PR) ; Get address of receive q header
CALL RQBRB ; Requeue the BRB (onto head of rcv queue)
CALL SETTRB ; Go set the TBA and RBA bits
OKINT
ITERX ; And pass the error upwards
SUBTTL RCVCBK - Receive completion callback from NISRV
XRESCD ; [7173]
RCVCBK: SAVEAC P3
LOAD P3,UNRID,(UN) ; Setup BR block pointer
STOR T3,BRSTA,(P3) ; Store the return status
OPSTR <DMOVE T1,>,UNSAD,(UN) ; Get source Ethernet address
OPSTR <DMOVEM T1,>,BRSAD,(P3) ; Put it in the buffer request block
OPSTR <DMOVE T1,>,UNDAD,(UN) ; Get destination Ethernet address
OPSTR <DMOVEM T1,>,BRDAD,(P3) ; Put it in the BR block
LOAD T1,UNPRO,(UN) ; Get protocol type
STOR T1,BRPRO,(P3) ; Put protocol type into the BR block
OPSTR <DMOVE T1,>,UNBFA,(UN) ; Get BP to buffer
OPSTR <DMOVEM T1,>,BRBFA,(P3) ; Put buffer pointer into BR block
LOAD T1,UNBSZ,(UN) ; Get length of datagram
STOR T1,BRBSZ,(P3) ; Put length into buffer request block
OPSTR <XMOVEI T1,>,PRRQH,(PR) ; Get receive queue header
CALL QUEBRB ; Put this one on the receive queue
CALL RCVPSI ; Queue went non-empty, give user a PSI
CALLRET ULKBUF ; Unlock the buffer
XSWAPCD ; [7173]
SUBTTL .EIEMA & .EIDMA - Enable/Disable a multicast address
NIUEMA: SKIPA T3,[NU.EMA] ; Get enable multicast function code
NIUDMA: MOVX T3,NU.DMA ; Get disable multicast function code
XCTU [OPSTR <DMOVE T1,>,EIAR1,(P1)] ; Fetch address to be enabled
ERJMP [ITERX] ; Pass the error upwards
OPSTR <DMOVEM T1,>,UNDAD,(UN) ; Put in where NISRV can find it
MOVE T1,T3 ; Fetch the appropriate function code
MOVE T2,UN ; Get pointer to UN block
CALL DLLUNI ; Set the address
JRST NIUEM1 ; Process the error
MRETNG ; All done!
NIUEM1: ADDX T1,DLLERB ; Convert NISRV error to user error
ITERR ; And pass it upwards
SUBTTL LOKBUF - Lock down user buffers
; This routine will lock down all memory associated with a user's buffer.
; The resulting locked pages will be stored in the BRCP1 and BRCP2 words
; of the BR block.
;
; Call: P3/ BR block pointer
; CALL LOKBUF
; <Return +1 Always>
LOKBUF: SETZRO BRCP2,(P3) ; Zero the locked core page numbers
OPSTR <DMOVE T1,>,BRBFA,(P3) ; Get byte pointer to buffer
IBP T1 ; Point to the real address
CALL BP2ADR## ; Convert it to an address
PUSH P,T1 ; Save the address
TXO T1,TWUSR ; Tell PAGEM this is a user mode addr
XCALL (MSEC1,MLKMA) ; [7173] Lock down the buffer
STOR T1,BRCP1,(P3) ; Remember the core page number
LOAD T1,BRBSZ,(P3) ; Get the buffer size
OPSTR <ADJBP T1,>,BRBFA,(P3) ; Point to the last byte of the buffer
CALL BP2ADR## ; Convert it to an address
POP P,T2 ; Get back the start address
XOR T2,T1 ; Compute the differences
TXNN T2,<007777,,777000> ; Start and end on the same page?
RET ; Yes, all done
TXO T1,TWUSR ; Tell PAGEM this is a user mode addr
XCALL (MSEC1,MLKMA) ; [7173] Nope, lock down the last page
STOR T1,BRCP2,(P3) ; Remember the end core page number
RET ; All done
SUBTTL ULKBUF - Unlock buffers
; Unlock the buffers pointed at by the BR block in P3. May be called at
; interrupt level.
XRESCD ; [7173] This must be resident
ULKBUF: OPSTR <SKIPN T1,>,BRCP1,(P3) ; Get the first core page number
IFSKP.
XCALL (MSEC1,MULKCR) ; [7173] Unlock the page
ENDIF.
OPSTR <SKIPN T1,>,BRCP2,(P3) ; Get the second core page number
IFSKP.
XCALL (MSEC1,MULKCR) ; [7173] Unlock the second core page
ENDIF.
RET ; If zero, we are done
XSWAPCD ; [7173] Go swappable again
SUBTTL DOINT - Process pending PSI's
; This routine will unlock the currently open portal, and enable PSI's so that
; the NI% JSYS can be interrupted. Immediately after enabling interrupts, this
; routine will disable them and (almost) restart the JSYS.
DOINT: OKINT ; Take the interrupt
ULOAD P2,BXNXT,(P2) ; Get pointer to next BXB
USTOR P2,EIBCP,(P1) ; Update the BXB pointer
JRST NI0 ; Restart the JSYS
SUBTTL USRCBK - Callback routine from NISRV
XRESCD ; [7173] This must be resident
USRCBK: SAVEAC <PR,UN>
MOVE UN,T2 ; Setup UN block pointer
LOAD PR,UNUID,(UN) ; Setup portal block pointer
JRST @CALDSP-1(T1) ; Dispatch on the function code
DEFINE XX (code)<TABENT NU.'code,<IFIW <code'CBK&777777>>> ; [7173]
CALDSP: TABBEG 1,NU.MAX,<IFIW <RTN&777777>> ; [7173]
XX XMT ; Transmit
XX RCV ; Receive
XX CLO ; Close
XX RCC ; Read Channel Counters
XX RPC ; Read Portal Counters
TABEND
XSWAPCD ; [7173] Back to swappable again
SUBTTL GETBRB & RETBRB - Get and Return Buffer Request blocks
; This routine will obtain a BR block. The memory will be resident.
; It takes no arguments, and returns a pointer to the memory in T1.
;
; Call: CALL GETBRB
; <+1 failure, couldn't get memory>
; <+2 success, T1/ pointer to BR block>
GETBRR: OPSTR <XMOVEI T1,>,PRRCQ,(PR) ; Pointer to receive quota
JRST GETBRB ; Join the main code stream
GETBRT: OPSTR <XMOVEI T1,>,PRTRQ,(PR) ; Pointer to transmit quota
GETBRB: SOSGE (T1) ; Quota exceeded?
JRST GETBRF ; Yes, process failure
SAVEAC P1 ; Get a good ac
MOVE P1,T1 ; Save pointer in case of error
MOVX T1,BR.LEN ; Allocate a BR block
CALL GXRCOR ; Get res memory for this request
AOSA (P1) ; Back up the quota and handle error
RETSKP ; Return success
MOVX T1,MONX05 ; No res free space
RET ; Give error return
GETBRF: SETZM (T1) ; Normalize the quota
MOVX T1,NIERTE ; Transmit or receive quota was exceeded
RET ; All done
GXRCOR: CALL .NIOFF ; Disable interrupts
SKIPN T2,TRCQUE ; Any command blocks available?
JRST GXRCO3 ; Nope, get more from ASGRES
XMOVEI T3,TRCQUE ; Setup previous address
GXRCO2: CAMG T1,-1(T2) ; Is this block big enough?
JRST GXRCO1 ; Yes, give it away
MOVE T3,T2 ; Remember previous block
MOVE T2,0(T2) ; Nope, point to next block
JUMPN T2,GXRCO2 ; And loop till done
GXRCO3: CALL .NION ; Turn interrupts back on
PUSH P,T1 ; Save Size for a moment
ADDI T1,1 ; Account for size word
HRLI T1,.RESP3 ; Low priority,,length of BR block
MOVX T2,.RESGP ; Use the general pool
XCALL (MSEC1,ASGRES) ; [7173] Get some free space
JRST PA1 ; Adjust the stack and return
POP P,(T1) ; Put the size into the block
ADDI T1,1 ; Point to the next word
RETSKP ; And return success
GXRCO1: MOVE T1,T2 ; Get block address into T1
MOVE T2,0(T2) ; Get next block address
MOVEM T2,0(T3) ; Make previous block point to next block
CALL .NION
RETSKP ; All done
RXRCOR: SETZM 0(T1) ; Zero the next block pointer
MOVE T2,TODCLK ; Get a timestamp
MOVEM T2,1(T1) ; Stamp the block
CALL .NIOFF ; No interrupts please
MOVE T2,TRCQUE ; Get the first block on the queue
MOVEM T2,0(T1) ; Make this block point to the first
MOVEM T1,TRCQUE ; Make this block be the new first
CALL .NION ; Allow interrupts
RET
RSI (TRCQUE,0)
RELBRR: OPSTR <XMOVEI T2,>,PRRCQ,(PR) ; Pointer to receive quota
JRST RELBRB ; Join the main code stream
RELBRT: OPSTR <XMOVEI T2,>,PRTRQ,(PR) ; Pointer to transmit quota
RELBRB: AOS (T2) ; Up the quota
CALLRET RXRCOR ; Release the memory
NIPIA==DLSCHN
XRESCD ; [7173]
.NIOFF::CONSO PI,PIPIIP ;[7454] Are we at interrupt level?
NOSKED ; No, disable the scheduler
CHNOFF NIPIA ; Disable the KLNI channel
RET ; And return to caller
ONRET:
.NION:: CHNON NIPIA ;[7454] Enable the KLNI interrupt channel
CONSO PI,PIPIIP ; Are we at interrupt level?
OKSKED ; No, don't forget to enable the scheduler
RET
XSWAPCD ; [7173]
; Here when closing portals, release all blocks from the block cache
RELCSH: SAVEAC P1
CALL .NIOFF ; No interrupts please
SKIPN P1,TRCQUE ; Get the queue head
JRST ONRET ; Q is empty, just return
RELCS1: MOVE T1,P1 ; Get block address into T1
MOVE P1,0(P1) ; Get next block address
SUBI T1,1 ; Point to true start of block
XCALL (MSEC1,RELRES) ; [7173] Release the memory
JUMPN P1,RELCS1 ; Loop over all blocks
SETZM TRCQUE ; Clear the q header
ONRSKP: CALL .NION ; All done, enable interrupts
RETSKP ; & give success return
SUBTTL QUEBRB - Queue up BRB's
; This routine will queue a buffer request block onto a queue pointed at by
; T1. This routine should be called with interrupts off.
;
; Call: MOVX P3,BR block
; MOVX T1,pointer to queue header
; CALL QUEBRB
; <+1 Queue was empty. Ie: It went from empty to non-empty.>
; <+2 Queue was not empty.>
XRESCD ; [7173]
QUEBRB: SETZRO BRNXT,(P3) ; Zero pointer to next BR block
LOAD T2,XRQUE,(T1) ; Get pointer to last item of queue
STOR P3,BRNXT,(T2) ; Make last block on q point to this block
STOR P3,XRQUE,(T1) ; Make q end point to our block
CAME T1,T2 ; Was the queue empty?
AOS (P) ; Nope, give skip return
RET ; Yes, non-skip return
XRESCD ; [7434]
; Get the first BRB on the queue
DQBRB: PIOFF ; Keep interrupt level away
LOAD P3,XRQUH,(T1) ; Get address of first BRB
JUMPE P3,DQBRB1 ; Jump if q is empty
LOAD T2,BRNXT,(P3) ; Get address of next BR block
STOR T2,XRQUH,(T1) ; Make it be the new first
OPSTR <XMOVEI T3,>,XRQUH,(T1) ; Get address of queue header
SKIPN T2 ; Is the queue now empty?
STOR T3,XRQUE,(T1) ; Yes, fix the end pointer
PION ; Turn interrupts back on
RETSKP ; Give successful return
DQBRB1: PION ; Turn interrupts back on
RET ; Give q empty return
; Requeue a BRB by putting it on the head of the queue
RQBRB: PIOFF ; No interrupts
LOAD T2,XRQUH,(T1) ; Get first item on queue
STOR T2,BRNXT,(P3) ; Make our BR block point to the next item
SKIPN T2 ; Was the queue empty?
STOR P3,XRQUE,(T1) ; Yes, make this item be the last
STOR P3,XRQUH,(T1) ; Make our block be the first
PION
RET
SUBTTL SETTRB - Set EITBA and EIRBA as appropriate
XSWAPCD ; [7434]
; This routine will set the Transmit Buffers Available and Receive Buffers
; Available bits as appropriate.
SETTRB: SETZ T1, ; Assume no transmits available
OPSTR SKIPE,PRXQH,+XR.QUH(PR) ; Any transmits available?
MOVX T1,1 ; Yes, say so
USTOR T1,EITBA,(P1) ; Tell the user about it
ERJMP [ITERX]
SETZ T1, ; Assume no receives available
OPSTR SKIPE,PRRQH,+XR.QUH(PR) ; Any receives available?
MOVX T1,1 ; Yes, say so
USTOR T1,EIRBA,(P1) ; Tell the user about it
ERJMP [ITERX]
RET
SUBTTL CREPOR - Create a Portal Block
; This routine creates a portal. It allocates a portal block and assigns
; the fork wide portal ID. It also creates and destroys the portal list
; structure. The process should be NOINT when calling this routine.
;
; Call: CALL CREPOR
; <+1 Couldn;t allocate memory>
; <+2 Success, PR points to portal block>
CREPOR: SAVEAC P1
SETZB UN,PR ; Indicate no blocks allocated yet
SKIPE P1,UPLIST ; Is the portal list setup?
JRST CREPO1 ; Yes, just create the portal block
MOVX T2,PL.LEN ; Get length of portal list structure
CALL GETJSF ; Allocate the memory
JRST [MOVX T1,MONX06 ; No swappable free space
JRST CREDIE] ; Couldn't get memory, clean up and go away
MOVEM T1,UPLIST ; Make the list official
MOVE P1,T1 ; Put list pointer in a safe place
CREPO1: LOAD T1,PLNUM,(P1) ; Get the number of portals defined so far
CAIL T1,MAXPOR ; Would we have too many portals?
JRST [MOVX T1,NIENRE ; No room for entry
JRST CREDIE]
MOVX T1,<.RESP3,,PR.LEN> ; Low priority,,length of PR block
MOVX T2,.RESGP ; Use the general pool
XCALL (MSEC1,ASGRES) ; [7173] Get some free space
JRST [MOVX T1,MONX05 ; No resident free space
JRST CREDIE]
MOVE PR,T1 ; Setup pointer to portal block
MOVX T1,1 ; Start with portal id # 1
OPSTR <XMOVEI T2,>,PLLIS,(P1) ; Get base address of portal list
CREPO3: SKIPN (T2) ; Is this slot in use?
JRST CREPO2 ; Nope, use it
DADD T1,[EXP 1,1] ; Increment address and count
CAIG T1,MAXPOR ; Did we run off the end of the portal list?
JRST CREPO3 ; Nope, try again
BUG. (HLT,NIJPMU,NIUSR,SOFT,<Portal List messed up>,<<P1,PRLIST>>,<
Cause: NIUSR was attempting to create a portal and install it in the portal
list. According to PLNUM, there were some free spots in the portal
list. An exhaustive search of the list was not able to find
a free slot. This is an inconsistency.
Data: PRLIST - Portal list address
>,CREDIE)
CREPO2: MOVEM PR,(T2) ; Make portal list point to portal block
OPSTRM <AOS>,PLNUM,(P1) ; Increment number of portals in use
OPSTR <CAMLE T1,>,PLMAX,(P1) ; Did we exceed the max?
STOR T1,PLMAX,(P1) ; Yes, setup the new max
STOR T1,PRUPD,(PR) ; Install the user's PID in the portal block
MOVX T2,UN.LEN ; Get length of a UN block
CALL GETJSF ; Get some free space
JRST [MOVX T1,MONX06 ; No swappable free space
JRST CREDIE]
MOVE UN,T1 ; Setup pointer to NISRV arg block
STOR UN,PRUNB,(PR) ; Make portal block point to UN block
;Link the Portal block onto the list beginning at UPRBAS
NOSKED ; No interrupts please
MOVE T1,UPRBAS ; Get the first item on the list
STOR T1,PRNXT,(PR) ; Make the new item point to the former 1st
MOVEM PR,UPRBAS ; Make the new item be the first
OKSKED
RETSKP ; And return success
CREDIE: MOVE P1,T1 ; Save error code
CALL KILPOR ; Kill the portal
MOVE T1,P1 ; Get error code
RET ; Pass it upwards
SUBTTL KILPOR - Release all resources associated with a portal
; Here to kill portals. Portal block pointer is in PR. This routine will
; release the portal block, it's UN block and deallocate the portal list
; if it's empty.
KILPOR: JUMPE PR,KILLIS ; If no PR block, try to kill portal list
; Here to remove the portal block from the portal list
LOAD T1,PRUPD,(PR) ; Get user's Portal ID
JUMPE T1,KILPO1 ; Jump if no PID assigned
; Remove the portal block from the portal list
ADD T1,UPLIST ; Point to the slot in the portal list
OPSTR <SETZM>,PLLIS,-1(T1) ; Clear the entry
OPSTRM <SOS T1,>,PLNUM,+@UPLIST ; Decrement number of portals in use
; Remove the portal block from the system wide portal list
XMOVEI T1,UPRBAS ; Get the base address
NOSKED ; No interrupts
KILPO2: OPSTR <CAMN PR,>,PRNXT,(T1) ; Does this block point to us?
JRST KILPO3 ; Yes, splice it out
LOAD T1,PRNXT,(T1) ; Point to the next portal
JRST KILPO2 ; Loop back
KILPO3: LOAD T2,PRNXT,(PR) ; Get our next pointer
STOR T2,PRNXT,(T1) ; Update his next pointer
SETZRO PRCHK,(PR) ; Zapp the check word to detect bugs
OKSKED ; Enable interrupts
; Now recompute PLMAX
MOVE T1,UPLIST ; Get address of portal list
OPSTR <ADD T1,>,PLMAX,+@UPLIST ; Point to last item in use
KILPO4: CAMLE T1,UPLIST ; Are we done?
SKIPE (T1) ; No, is this entry blank?
TRNA ; Done or not blank
SOJA T1,KILPO4 ; Yes, try again
SUB T1,UPLIST ; Compute highest used PID
STOR T1,PLMAX,+@UPLIST ; Save it for posterity
; Here to delete a portal block and it's associeted UN block
KILPO1: OPSTR <SKIPE T2,>,PRUNB,(PR) ; Get the UN block address
CALL GIVJSF ; Release the UN block
MOVE T1,PR ; Get pointer to portal block
XCALL (MSEC1,RELRES) ; [7173] Release the portal block
; Here to delete the portal list if it's empty
KILLIS: LOAD T2,PLNUM,+@UPLIST ; Get the number of portal blocks defined
JUMPN T2,RTN ; Just return if some portals are left
EXCH T2,UPLIST ; Clear pointer, get address of portal list
CALLRET GIVJSF ; Release the portal list
SUBTTL NIJKFK - Reset for the NI% JSYS
; Come here from KFORK% (actually KSELF), RESET% and LGOUT%. This must be
; called in the fork's context.
XRENT (NIJKFK,G) ; [7434] NIJKFK:: and XNIJKF::
MOVX T1,0 ; Get a channel #
NOSKED ; No interrupts please
CALL CHKOW1 ; Do we own this KLNI?
TRNA ; Nope, skip this
SETOM KNIOWN ; Yes, clear ownership
OKSKED ; Allow interrupts
SKIPN UPLIST ; Is there a portal table?
RET ; Nope, all done
SAVEAC <P1,P2,PR,UN>
NOINT ; No PSI's please
SKIPN T1,UPLIST ; Get the portal list pointer
JRST NIJKF9 ; All done, list sneaked away
LOAD P1,PLMAX,(T1) ; Get the highest portal defined
OPSTR <XMOVEI P2,>,PLLIS,(T1) ; Get pointer to list beginning
NIJKF3: SKIPN PR,(P2) ; Get a portal block
JRST NIJKF2 ; No such portal, try next slot
LOAD UN,PRUNB,(PR) ; Setup UN block pointer
CALL NIUCLI ; Close the portal
NIJKF2: ADDI P2,1 ; Point to next entry
SOJG P1,NIJKF3 ; Loop over all portals
NIJKF9: OKINT ; Allow PSI's again
RET
SUBTTL ***** Hacks *****
; Hacks of the century go here!
; This is a cover routine for the stupid ASGJFR routine. This routine
; allocates one more word and increments the address before returning it
; to the caller. Arguments identical to ASGJFR.
GETJSF: ADDI T2,1 ; Get one more word
XCALL (MSEC1,ASGJFR) ; [7173] Get the memory
RET ; Sigh, can't get it
AOJA T1,RSKP ; Point to next word and return success
GIVJSF: SUBI T2,1 ; Point to header word
XCALLRET (MSEC1,RELJFR) ; [7173] All done
SUBTTL .EIRCI - Read Channel Information
XSWAPCD ;[7434]
NIURCI: STKVAR (<<UNBLOK,UN.LEN>>) ; Allocate a UN block
ULOAD T1,EICHN,(P1) ; Get the ethernet channel #
STOR T1,UNCHN,+UNBLOK ; Put the channel number into the UN block
MOVX T1,NU.RCI ; Get function code
XMOVEI T2,UNBLOK ; Get UN block address
CALL DLLUNI ; Read the channel's info
JRST DLLERR ; Go handle a DLL error
LOAD T1,UNSTA,+UNBLOK ; Read the status word from the UN block
USTOR T1,EISTA,(P1) ; Store it in the user's block
OPSTR <DMOVE T1,>,UNCAR,+UNBLOK ; Get the current address
XCTU [OPSTR <DMOVEM T1,>,EIPHY,(P1)] ; Return it to the user
OPSTR <DMOVE T1,>,UNHAD,+UNBLOK ; Get the ROM address
XCTU [OPSTR <DMOVEM T1,>,EIHRD,(P1)] ; Return hardware addr to the user
MRETNG ; Return success
ENDSV. ; Terminate the STKVAR
DLLERR: ADDI T1,DLLERB ; Generate offset for NISRV errors
ITERR ; Pass the error (in T1) upwards
SUBTTL .EISCA - Set Channel Address
NIUSCA: STKVAR (<<UNBLOK,UN.LEN>>) ; Allocate a UN block
ULOAD T1,EICHN,(P1) ; Get the ethernet channel #
STOR T1,UNCHN,+UNBLOK ; Put the channel number into the UN block
XCTU [OPSTR <DMOVE T1,>,EIPHY,(P1)] ; Fetch the new address
OPSTR <DMOVEM T1,>,UNDAD,+UNBLOK ; Put address into NISRV arg block
SETZRO UNPTR,+UNBLOK ; Indicate we have an immediate address
MOVX T1,NU.SCA ; Get function code
XMOVEI T2,UNBLOK ; Get arg block address
CALL DLLUNI ; Go tell NISRV to set the address
JRST DLLERR ; Convert the error code and return
MRETNG ; All done!
ENDSV. ; Terminate the STKVAR
SUBTTL .EISCS - Set Channel State
NIUSCS: CALL CHKOWN ; Do we own this channel?
ITERR ; Nope, pass up the error
STKVAR (<<UNBLOK,UN.LEN>>)
ULOAD T1,EICHN,(P1) ; Get the ethernet channel #
STOR T1,UNCHN,+UNBLOK ; Put the channel number into the UN block
ULOAD T1,EISST,(P1) ; Get the new state
STOR T1,UNSST,+UNBLOK ; Save the new state
MOVX T1,NU.SCS ; Get function code
XMOVEI T2,UNBLOK ; Get UN block address
CALL DLLUNI ; Set the channel's state
JRST DLLERR ; Go handle a DLL error
MRETNG ; Return success
ENDSV. ; Terminate the STKVAR
SUBTTL .EIRCC & .EIRPC - Read Channel & Portal Counters
NIURPC: TDZA T1,T1 ; Indicate reading portal counters
NIURCC: SETO T1, ; Indicate reading channel counters
NOINT ; No PSI's please
TRVAR (<<UNBLOK,UN.LEN>,<CCBLOK,CC.LEN>,CHNPOR>)
MOVEM T1,CHNPOR ; Set flag to indicate read channel counters
NIURC1: SETZRO UNPID,+UNBLOK ; Clear UNPID in case of errors
SETZRO UNBFA,+UNBLOK ; Clear UNBFA in case of errors
SETO T1, ; Get protocol type for informational portal
STOR T1,UNPRO,+UNBLOK ; Put it in the UN block
XMOVEI T1,USRCBK ; Get the callback address
STOR T1,UNCBA,+UNBLOK ; Put it in the UN block
SETZRO UNUID,+UNBLOK ; Setup the user ID
MOVX T1,NU.OPN ; Get function code (Open a Portal)
XMOVEI T2,UNBLOK ; Get UN block address
CALL DLLUNI ; Open the informational portal
JRST RCCDI1 ; Die gracefully
MOVX T1,<.RESP3,,CC.LEN> ; Low priority,, length of counters block
SKIPN CHNPOR ; Reading portal counters?
MOVX T1,<.RESP3,,PC.LEN> ; Yes, use proper length
MOVX T2,.RESGP ; Use the general pool
XCALL (MSEC1,ASGRES) ; [7173] Get some free space
JRST [MOVX T1,MONX05 ; Can't, release it
JRST RCCDI3] ; And kill the information portal
STOR T1,UNBFA,+UNBLOK ; Save the buffer address
MOVX T1,CC.LEN ; Get the buffer length
SKIPN CHNPOR ; Reading portal counters?
MOVX T1,PC.LEN ; Yes, use this length instead
STOR T1,UNBSZ,+UNBLOK ; Put it in the UN block
ULOAD T1,EICHN,(P1) ; Get the channel #
ERJMP RCCDI2 ; Clean up and pass the error up
STOR T1,UNCHN,+UNBLOK ; Put the channel # into the UN block
ULOAD T1,EIZRO,(P1) ; Get the zero counters bit
ERJMP RCCDI2 ; Clean up and pass the error upwards
STOR T1,UNZRO,+UNBLOK ; Put the zero counters bit into the UN block
SKIPE CHNPOR ; Are we reading portal counters?
IFSKP. ; Yes
CALL FNDGPR ; Look for a global portal ID
JRST RCCDI3 ; No good, pass error upwards
STOR T1,UNSPI,+UNBLOK ; Save the object portal ID
ENDIF. ; End of Read Portal Counters code
MOVE T1,FORKX ; Get our fork #
STOR T1,UNRID,+UNBLOK ; Save the fork number for callback
SETZRO FKST2,(T1) ; Clear completion flag for scheduler test
MOVX T1,NU.RCC ; Function is read channel counters
SKIPN CHNPOR ; Read channel counters?
MOVX T1,NU.RPC ; No, read portal counters
XMOVEI T2,UNBLOK ; Get the UN block address
CALL DLLUNI ; Read the counters
JRST RCCDI1 ; Process the errors
MOVEI T1,RCCTST ; Get the scheduler test for read counters
MDISMS ; Dismiss till counters are read
MOVE T1,FORKX ; Get our fork #
MOVE T1,FKINT(T1) ; Get the interrupt bits
TXNN T1,FKPS1 ; Interrupt pending?
IFSKP.
CALL RCCRES ; Clear all resources
OKINT ; Allow interrupts for a moment
NOINT ; Clam it up again
JRST NIURC1 ; And restart the JSYS
ENDIF.
; Now copy all counters from the monitor to the user
SAVEAC <P2,P3,P4> ; Save a few AC's
ULOAD P2,EIAR1,(P1) ; Get the counter buffer size
ERJMP RCCDI2 ; Die
SUBI P2,1 ; Account for length word
SKIPLE P2 ; Any room?
IFSKP.
MOVX T1,NIEIBS ; Nope, complain
JRST RCCDI3 ; But clean up first
ENDIF.
MOVX T1,CTRTLN ; Get the # of counters we know about
SKIPN CHNPOR ; Reading channel counters?
MOVX T1,PRCTLN ; No, we are reading portal counters
CAMLE P2,T1 ; Is the user's buffer too big?
MOVE P2,T1 ; Yes, use the shorter length
ULOAD P3,EIAR2,(P1) ; Get the addr of the counter buffer
ERJMP RCCDI2
UMOVEM P2,.ECCNT(P3) ; Save the returned length
ERJMP RCCDI2
ADDI P3,1 ; Point to the next word of the counters block
LOAD P4,UNBFA,+UNBLOK ; Get the address of the counter table
MOVN P2,P2 ; Negate the count
HRLZ P2,P2 ; Get count into left half
HRRI P2,CTRTBL ; Get table address into right half
SKIPN CHNPOR ; Read channel counters?
HRRI P2,PRCTBL ; No, use table for read portal counters
CTRLOP: XCT (P2) ; Fetch an item from the counters block
UMOVEM T1,(P3) ; Put it in the user's table
ERJMP RCCDI2
ADDI P3,1 ; Point to the next user loc
AOBJN P2,CTRLOP ; Loop over the whole counter table
CALL RCCRES ; Release all the resources
OKINT ; Make us interruptable again
MRETNG ; And return success
RESCD ; Make scheduler test resident
RCCTST: JN FKPS1,(FX),RSKP ; Wakeup if a PSI is pending
OPSTR SKIPN,FKST2,(FX) ; Have we gotten the callback yet?
RET ; Nope, block some more
RETSKP ; Yes, wake him up
; Here from NISRV callback level
RCCCBK: ; Callback for Read Channel Counters
RPCCBK: ; Callback for Read Portal Counters
LOAD T1,UNRID,(UN) ; Get the fork #
SETONE FKST2,(T1) ; Indicate that the counters have been read
RET
XSWAPCD ; [7173]
; Here when we get an error from NISRV
RCCDI1: ADDI T1,DLLERB ; Convert it to a real error code
RCCDI3: MOVEM T1,LSTERR ; Make it official
RCCDI2: CALL RCCRES ; Release all the resources
ITERX ; Return with error code in LSTERR
; Here to release all resources associated with this function
RCCRES: OPSTR <SKIPN T1,>,UNBFA,+UNBLOK ; Get the counters buffer address
IFSKP.
XCALL (MSEC1,RELRES) ; [7173] Release the memory
ENDIF.
MOVX T1,NU.CLO ; Close the portal
XMOVEI T2,UNBLOK ; Get the UN block address
OPSTR SKIPN,UNPID,+UNBLOK ; Did we get a PID?
IFSKP.
CALL DLLUNI ; Close the portal
TRN
ENDIF.
RET
ENDTV. ; Terminate the TRVAR
DEFINE FOO (field,value)<PRINTX ? CTRTBL out of order field'='value>
DEFINE XX (field)<
IFN .-CTRTBL-.EC'field+.ECSLZ,FOO (field,\.EC'field)
LOAD T1,CC'field,(P4) ;; Load the field
>
IFN .ECCNT-.EPCNT,<PRINTX ? .ECCNT and .EPCNT are not equal>
CTRTBL:
XX SLZ ; Seconds since last zeroed
XX BYR ; Bytes received
XX BYS ; Bytes sent
XX DGR ; Datagrams received
XX DGS ; Datagrams sent
XX MBR ; Multicast bytes received
XX MDR ; Multicast datagrams received
XX DSD ; Datagrams sent, initially deferred
XX DS1 ; Datagrams sent, single collision
XX DSM ; Datagrams sent multiple collisions
XX SF ; Send failures
XX SFM ; Send failure bit mask
XX RF ; Receive failure
XX RFM ; Receive failure bit mask
XX UFD ; Unrecognized frame destination
XX DOV ; Data overrun
XX SBU ; System buffer unavailable
XX UBU ; User buffer unavailable
CTRTLN==.-CTRTBL
DEFINE FOO (field,value)<PRINTX ? PRCTBL out of order field'='value>
DEFINE XX (field)<
IFN .-PRCTBL-.EP'field+.EPSLZ,FOO (field,\.EP'field)
LOAD T1,PC'field,(P4) ;; Load the field
>
PRCTBL: XX SLZ ; Seconds since last zeroed
XX BYR ; Bytes received
XX DGR ; Datagrams received
XX BYS ; Bytes sent
XX DGS ; Datagrams sent
XX UBU ; User buffer unavailable
PRCTLN==.-PRCTBL
SUBTTL .EIGET and .EIREL - Get and release a KLNI
; Test and set the ownership of a KLNI in an uninterruptable manner.
NIUGET: ULOAD T1,EICHN,(P1) ; Obtain the channel #
NOSKED ; Do this uninterruptably
CALL CHKOW1 ; Anybody own this channel already?
JRST [OKSKED ; Allow interrupts
ITERR] ; Channel is already in use
MOVE T2,FORKX ; Get our system wide fork index
MOVEM T2,KNIOWN ; Make us be the owner
OKSKED ; Allow interrupts
MRETNG ; All done!
; Release ownership of the KLNI
NIUREL: NOINT ; Don't allow PSI's
CALL CHKOW1 ; Do we own this channel?
ITERR ; Nope, don't allow this
SETOM KNIOWN ; Clear our ownership
OKINT ; Allow PSI's
MRETNG ; Return good
SUBTTL .EIRCL - Read Channel List
NUMLST==50 ; Amount of space to allocate for lists
; This macro should be used for all things that want a common TRVAR that
; defines UNBLOK and CLBLOK.
DEFINE TRSTUF <TRVAR (<<UNBLOK,UN.LEN>,<CLBLOK,NUMLST>>)>
NIURCL: TRSTUF ; Define common TRVARs
MOVX T1,NU.RCL ; Get function code
CALL REDLST ; Go read the list
; Now copy the channel list into the user's buffer
CALL CPYLST ; Copy the channel list to the user
MRETNG ; All done!
; Do an NISRV Read Channel List or Read Portal List function
REDLST: XMOVEI T2,CLBLOK ; Get address to hold the list
STOR T2,UNBFA,+UNBLOK ; Put it in the arg block
MOVX T2,NUMLST ; Get length of list
STOR T2,UNBSZ,+UNBLOK ; Put length into the UN block
XMOVEI T2,UNBLOK ; Get arg block address
CALL DLLUNI ; Read the list
JRST [ADDI T1,DLLERB ; Convert to a real error code
ITERR] ; And give error return
RET ; All done!
; Copy the list in the CLBLOK TRVAR to the user
CPYLST: ULOAD T1,EIAR1,(P1) ; Get the user's buffer size
SKIPG T1 ; Is the size reasonable?
ITERR NIEIBS ; Nope, invalid buffer size
ULOAD T3,EIAR2,(P1) ; Get the buffer address
XMOVEI T2,CLBLOK ; Get address of the list
OPSTR <CAMLE T1,>,UNBSZ,+UNBLOK ; Is the user's buffer too big?
LOAD T1,UNBSZ,+UNBLOK ; Yes, use the smaller size
UMOVEM T1,(T3) ; Install the # of items we will return
ADDI T3,1 ; Point to the next item
XCTBMU [EXTEND T1,[XBLT]] ; Copy the data to the user
RET ; All done!
ENDTV. ; End the TRVAR
SUBTTL .EIRPL - Read Portal List
NIURPL: SAVEAC <P2,P3>
TRSTUF ; Define common TRVARs
ULOAD T1,EIGBL,(P1) ; Get the global portal ID bit
SKIPN T1 ; Doing global portal ID's?
IFSKP. ; Yes
MOVX T1,NU.RPL ; Tell NISRV to return it's portal list
CALL REDLST ; Go read the list
XMOVEI P2,CLBLOK ; Get address of the channel list
XMOVEI P3,CLBLOK ; Setup list filler AC
OPSTR <SKIPN T1,>,UNBSZ,+UNBLOK ; Get number of entries
JRST NIURP1 ; Jump if no entries
MOVN T1,T1 ; Make it negative
HRL P2,T1 ; Form an AOBJN pointer
DO.
MOVE T1,(P2) ; Get an NISRV PID
CALL INTOEX ; Convert it to an NIUSR PID
IFSKP.
MOVEM T1,(P3) ; Put back the portal ID
ADDI P3,1 ; Point to next output
ENDIF.
AOBJN P2,TOP. ; Loop over the whole mess
ENDDO.
XMOVEI T1,CLBLOK ; Get base addr of portal list
SUB P3,T1 ; Compute number of ID's returned
STOR P3,UNBSZ,+UNBLOK ; Setup for CPYLST
ELSE. ; Local portal ID's
NOINT ; No interrupts while traversing list
SKIPE T1,UPLIST ; Get base of portal list
IFSKP. ; No portals defined
OKINT ; Allow interrupts
SETZRO UNBSZ,+UNBLOK ; No portals IDs returned
JRST NIURP1 ; Exit gracefully
ENDIF.
OPSTR <XMOVEI P2,>,PLLIS,(T1) ; Get address of base of portal list
LOAD T4,PLMAX,(T1) ; Get number of PIDs to scan
XMOVEI P3,CLBLOK ; Get address of output buffer
STOR P3,UNBFA,+UNBLOK ; Put it where CPYLST can find it
DO.
JUMPE T4,ENDLP. ; Jump if no more portals
SKIPN T1,(P2) ; Fetch a portal address
IFSKP. ; Found a portal
LOAD T2,PRUPD,(T1) ; Get the portal ID
MOVEM T2,(P3) ; Install this portal ID
ADDI P3,1 ; Point to next output buffer add
ENDIF.
ADDI P2,1 ; Point to next portal ID
SOJA T4,TOP. ; Loop over all the portals
ENDDO.
XMOVEI T1,CLBLOK ; Get start address of output buffer
SUB P3,T1 ; Compute number of portal ID's returned
STOR P3,UNBSZ,+UNBLOK ; Put it where CPYLST will find it
OKINT
ENDIF.
NIURP1: CALL CPYLST ; Copy the list to the user
MRETNG ; And return success to the user
ENDTV. ; Terminate the TRVAR
SUBTTL INTOEX - Convert internal portal ID's to external portal ID's
; Convert the internal portal ID's to external portal ID's
INTOEX: TRVAR (<<UNBLOK,UN.LEN>>)
STKVAR <TEMPAC> ; [7285]
STOR T1,UNPID,+UNBLOK ; Setup portal ID
SETZRO UNBSZ,+UNBLOK ; We don't want to know about multicasts
MOVX T1,NU.RPI ; Read portal info
XMOVEI T2,UNBLOK ; Get arg block addr
CALL DLLUNI ; Read the portal's info
RET ; Pass the error upwards
LOAD T1,UNCBA,+UNBLOK ; Get the callback address
XMOVEI T2,USRCBK ; Get our callback address
CAME T1,T2 ; Match?
IFSKP. ; Yes, convert to a real portal ID
SETZ T1, ; Reset the portal ID
LOAD T2,UNUID,+UNBLOK ; Get the NIUSR portal block address
NOSKED ; Keep portals from sneaking away
OPSTR <CAME T2,>,PRCHK,(T2) ; Legit portal?
JRST ONRET ; Nope, must have snuck away...
LOAD T3,PRUPD,(T2) ; Get his fork-wide portal ID
STOR T3,PIPID,+T1 ; Add that to the global portal ID
LOAD T3,PRLFK,(T2) ; Get the FORKN of the portal
STOR T3,PIFRK,+T1 ; Setup the fork #
LOAD T3,PRFRK,(T2) ; Get the FORKX of the portal
OKSKED ; Ok, portal can sneak away now
MOVEM T1,TEMPAC ; [7285] Save T1
LOAD T1,FKJO%,(T3) ; [7285] Get the job # of this portal
CALL LCL2GL ; [7285] Get global job number
SETOM T1 ; [7285] Failed, use -1
HRLZS T1 ; [7285] Move to LH
HRR T1,TEMPAC ; [7285] Get back fork and portal ID
ELSE. ; No, just return NISRV's external portal ID
LOAD T1,UNEXI,+UNBLOK ; Fetch the external portal ID
HRLI T1,-1 ; Use job # of -1 for monitor portals
ENDIF.
RETSKP ; Return success
ENDTV. ; Terminate the TRVAR
SUBTTL .EIRPI - Read Portal Info
NIURPI: TRSTUF ; Define common TRVARs
CALL FNDGPR ; Find the NISRV portal he's talking about
ITERR ; Couldn't find it, bomb the user
STOR T1,UNPID,+UNBLOK ; Put portal ID where NISRV can find it
XMOVEI T1,CLBLOK ; Get address of multicast list
STOR T1,UNBFA,+UNBLOK ; Put it into NISRV's arg block
MOVX T1,NUMLST ; Get length of list
STOR T1,UNBSZ,+UNBLOK ; Put multicast list length in NISRV arg block
MOVX T1,NU.RPI ; Get function code for NISRV
XMOVEI T2,UNBLOK ; Setup arg block address for NISRV
CALL DLLUNI ; Read some info about this portal
JRST DLLERR ; Pass error to the user
LOAD T1,UNPRO,+UNBLOK ; Get the protocol type
USTOR T1,EIPRO,(P1) ; Return protocol type to the user
LOAD T1,UNPAD,+UNBLOK ; Get the pad flag
USTOR T1,EIPAD,(P1) ; Return pad flag to the user
LOAD T1,UNCHN,+UNBLOK ; Get the Ethernet channel number
USTOR T1,EICHN,(P1) ; Return the channel number to the user
LOAD T1,UNCBA,+UNBLOK ; Get the callback address
XMOVEI T2,USRCBK ; Get our callback address
CAME T1,T2 ; Is it an NIUSR portal?
IFSKP. ; Yes
LOAD T1,UNUID,+UNBLOK ; Get the user ID
NOSKED ; Keep portal from slipping away
OPSTR <CAME T1,>,PRCHK,(T1) ; Is the check word valid?
IFSKP. ; Yes
LOAD T2,PRTCH,(T1) ; Get the xmit channel PSI
STOR T2,EITCH,T3-$EITCH ; Put it into T3
LOAD T2,PRRCH,(T1) ; Get the receive channel PSI
STOR T2,EIRCH,T3-$EIRCH ; Put it into T3
LOAD T2,PRSCH,(T1) ; Get the status channel PSI
STOR T2,EISCH,T3-$EISCH ; Put it into T3
ENDIF.
OKSKED ; Allow scheduler to run
ELSE.
SETZ T3, ; No PSI's if not NIUSR portal
ENDIF.
USTOR T3,EIPSI,(P1) ; Return the PSI's
XCTU [OPSTR <SKIPG T1,>,EIAR1,(P1)] ; Did user supply a multicast buffer?
IFSKP. ; Yes.
SUBI T1,1 ; Account for count word
LSH T1,-1 ; Compute # of multicasts user has room for
HLRZ T2,CLBLOK ; Get number of multicasts returned
CAMLE T1,T2 ; Did user supply enough space?
MOVE T1,T2 ; Yes, user was generous, use monitor's count
HRLM T1,CLBLOK ; Save # of multicasts returned to the user
LSH T1,1 ; Multiply by 2
ADDI T1,1 ; Add in count word
XMOVEI T2,CLBLOK ; Get source address for XBLT
ULOAD T3,EIAR2,(P1) ; Get dest addr for XBLT
XCTBMU [EXTEND T1,[XBLT]] ; Copy the data to the user
ENDIF.
MRETNG ; All done!
ENDTV.
SUBTTL CHKOWN - Check ownership of KLNI
; See if the current fork owns the Ethernet channel in EICHN.
;
; Call: CALL CHKOWN
; <Return +1, channel is owned by another fork, T1/ channel #>
; <Return +2, channel is unowned, or owned by this fork T1/ Channel #>
;
; CHKOW1 - Same as CHKOWN, but T1 contains channel #
CHKOWN: ULOAD T1,EICHN,(P1) ; Get the channel #
ERJMPR RTN ; Pass the error code upwards
CHKOW1: SKIPGE KNIOWN ; Anybody own the KLNI?
RETSKP ; Nope, give skip return
MOVE T2,FORKX ; Get our fork number
CAME T2,KNIOWN ; Is this fork the owner?
RETBAD (NIECIO) ; Nope, give error return
RETSKP ; Yes, skip return
SUBTTL FNDPOR - Find the portal database associated with a portal ID
; FNDPOR - Find the portal block associated with a local portal ID.
;
; Call: MOVX P1,previous context address of EI block
; CALL FNDPOR
; <Return +1, error code in T1>
; <Returns +2, portal block pointer in T1>
;
; Uses only T1 and T2
FNDPOR: SKIPN T2,UPLIST ; Get the pointer to his portal list
RETBAD NIENSP ; No such portal
ULOAD T1,EIPID,(P1) ; Fetch his Portal ID
ERJMPR RTN ; Propagate the error
OPSTR <CAMG T1,>,PLMAX,(T2) ; Is it larger than the largest?
SKIPG T1 ; Or .LE. 0?
RETBAD NIENSP ; No such portal
ADD T2,T1 ; Add portal offset to portal list base
OPSTR <SKIPN T1,>,PLLIS,-1(T2) ; Fetch address of our portal block
RETBAD NIENSP ; No such portal
OPSTR <CAME T1,>,PRCHK,(T1) ; Should be equal to the PR block address
BUG. (HLT,NIJIPB,NIUSR,SOFT,<Illegal Portal Block>,<<T1,JOBPR>>,<
Cause: NIUSR did not find a proper portal block pointer in the job's portal
list.
Data: JOBPPR - Job's portal list address
>,RTN)
RETSKP
SUBTTL FNDGPR - Find NISRV portal IDs
; FNDGPR - Find the NISRV portal ID associated with the possibly global
; portal ID in EIPID.
;
; Call: MOVX P1,previous context address of EI block
; CALL FNDGPR
; <Return +1, error code in T1>
; <Returns +2, portal block pointer in T1>
;
FNDGPR: ULOAD T1,EIGBL,(P1) ; Get the global flag
ERJMPR RTN
JUMPE T1,FNDLOC ; Jump if just local portal ID
ULOAD T1,EIJOB,(P1) ; Get the job number
ERJMPR RTN
HRRE T1,T1 ; Extend the sign
CAMN T1,[-1] ; Job -1?
JRST FNDMON ; Yes, look through monitor's portals
; Here to find portals in another job or fork
CALL GL2LCL ; Convert it to an internal job index
RET ; Pass error upwards
ULOAD T2,EIPID,(P1) ; Get the portal ID
ERJMPR RTN
LOAD T3,PIFRK,+T2 ; Get the job relative fork #
LOAD T2,PIPID,+T2 ; Get the fork relative pid #
SAVEAC P2
NOSKED ; No interrupts
MOVE P2,UPRBAS ; Get the head of the portal list
TRNA
FNDGLP: LOAD P2,PRNXT,(P2) ; Get the next portal
JUMPE P2,FNDGL1 ; Jump when done
OPSTR <CAME T2,>,PRUPD,(P2) ; Does the local portal ID match?
JRST FNDGLP ; Nope, try next portal
OPSTR <CAME T3,>,PRLFK,(P2) ; Does the local fork # match?
JRST FNDGLP ; Nope, try next portal
LOAD T4,PRFRK,(P2) ; Get his fork number
LOAD T4,FKJO%,(T4) ; Get his job #
CAME T4,T1 ; Does his job # match?
JRST FNDGLP ; Nope, try next portal
LOAD T1,PRPID,(P2) ; Get his NISRV portal ID
OKSKED
RETSKP ; And return success
FNDGL1: OKSKED ; Allow interrupts
RETBAD NIENSP ; Give error - No Such Portal
; Here for portals owned by this fork
FNDLOC: CALL FNDPOR ; Lookup our portal ID
RET ; Pass error upwards
LOAD T1,PRPID,(T1) ; Get the NISRV portal ID
RETSKP ; And return success
; Here for monitor based portals
FNDMON: TRVAR (<<UNBLOK,UN.LEN>>) ; Make room for portal list
ULOAD T1,EIPID,(P1) ; Get the portal ID
ERJMPR RTN
STOR T1,UNEXI,+UNBLOK ; Setup the external portal ID
MOVX T1,NU.CXI ; Convert to internal PID
XMOVEI T2,UNBLOK ; Get arg block address
CALL DLLUNI ; Convert to a real PID
RETBAD (,<ADDI T1,DLLERB>) ; Portal probably went away
SETZRO UNBSZ,+UNBLOK ; We don't want the multicasts
MOVX T1,NU.RPI ; Function is Read Portal Info
XMOVEI T2,UNBLOK ; Get the arg block address
CALL DLLUNI ; Get some info on the portal
RETBAD (,<ADDI T1,DLLERB>) ; Portal probably went away
LOAD T1,UNCBA,+UNBLOK ; Get the callback address
XMOVEI T2,USRCBK ; Get our callback address
CAMN T1,T2 ; Are they the same?
RETBAD (NIENSP) ; Yes, there are other ways to get
; info about this portal.
LOAD T1,UNPID,+UNBLOK ; Get the NISRV portal ID
RETSKP ; And return success
ENDTV. ; Terminate the TRSVR.
SUBTTL SMON% JSYS - Set NI address
; Call: MOVX T1,.SFSEA ; Function code is "Set Ethernet Address"
; MOVX T2,chnnum ; Ethernet interface channel number
; MOVE T3,[POINT 8,ADDR] ; Byte pointer to 6 byte Ethernet address
; SMON% ; Set the address
XRENT (SETNIA,G) ; [7434] SETNIA:: and XSETNI::
STKVAR (<<EIBLOK,.EIBMX>>) ; Allocate an NI% JSYS arg block
UMOVE T1,2 ; Get Ethernet channel number
STOR T1,EICHN,+EIBLOK ; Stuff it in the arg block
SETO T1, ; Get a -1
XCTU [CAMN T1,3] ; Did user use -1 instead of a byte pointer?
JRST SETNI3 ; Yes, go set the DECnet address
MOVX T4,6 ; Fetch six bytes
OPSTR <XMOVEI T3,>,EIPHY,+EIBLOK ; Setup destination address
TXO T3,OWGP.(8) ; Make it an 8 bit one word global
DO.
XCTBUU [ILDB T1,3] ; Fetch a byte from the user
IDPB T1,T3 ; Put it in the UN block
SOJG T4,TOP. ; Loop over all six bytes
ENDDO.
SETNI4: MOVX T1,.EISCA ; Function is "Set Channel address"
STOR T1,EIFCN,+EIBLOK ; Put it in the arg block
XMOVEI T1,+EIBLOK ; Get the arg block address
NI% ; Set the address
ERJMP .+1 ; Ignore errors for now
RET
; Here to set the DECnet address for the Ethernet channel
SETNI3: MOVE T1,RTRHIO## ; Get HI-ORD
MOVE T2,RTRHOM## ; Get it's area number
LSH T2,^D10 ; Put it into place
IOR T2,RTRADR## ; Get LO-ORD
DPB T2,[POINT 8,T2,19] ; Swap the bytes
LSH T2,^D12 ; Left justify the swapped address
OPSTR <DMOVEM T1,>,EIPHY,+EIBLOK ; Put the address into the arg block
JRST SETNI4 ; And finish up
ENDSV. ; Terminate the STKVAR
SUBTTL End of NIUSR
TNXEND
END