Google
 

Trailing-Edge - PDP-10 Archives - TOPS-20_V6.1_DECnetSrc_7-23-85 - mcb/sc/scxdsp.bli
There is 1 other file named scxdsp.bli in the archive. Click here to see a list.
module SCXDSP (				! RSX User Interface Module
		ident = 'X01340'
		) =
begin
!
!                    COPYRIGHT (c) 1980, 1981, 1982
!                    DIGITAL EQUIPMENT CORPORATION
!                        Maynard, Massachusetts
!
!     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  which  is  not supplied by
!     DIGITAL.
!



!++
!
! FACILITY:	Session Control
!
! ABSTRACT:	This module handles all I/O packets originating from
!		the user, destined for Session Control.  The I/O packets
!		are handled like those in a device service routine.
!		CCBs arriving from Session Control are also handled here.
!
! ENVIRONMENT:	MCB
!
! AUTHOR:	Buren Hoffman		CREATION DATE: 1-Aug-80
!
! MODIFIED BY:
!	X01010	Changed code to use $byt_ptr macro in place of the
!		ch$ptr macro.
!	X01020	Fixed faulty dispatch vector processing, and
!		fixed xportability problems, concerning number of
!		bytes per word.
!	X01030	Corrected memory allocation problem in U$OPN.
!	X01040	Updated signal_stop processing to show more info.
!	X01050	Provide own queueing - can't use the CCB queueing
!		facilities in MCB, because they are for CCBs only.
!	X01060	Fixed byte-vector handling problem in user interface.
!	X01070	Use new Comm/Exec to process linkage (.CRDAT for database)
!	X01080	Upgraded GLN support to return node addresses, ...
!	X01090	Updated to use library calls, instead of requires.
!	X01100	Fixed return to RSX so that RSX$ call is done before
!		returning.
!	X01110	Repaired bug in LUN number calculation.
!	X01120	Numerous minor bug fixes.
!	X01130	Added call to AST queueing routine.
!	X01140	Put in bullet proofing to keep user from using
!		unopened link.
!	X01150	Fixed resource wait dispatch vectors.
!	X01160	Fixed disconnect/reject/timeout processing to
!		keep proper count of active links.
!	X01170	Fix for memory and link management in abort/disconnect
!		processing.
!	X01180	?
!	X01190	Changed code in user abort or disconnect to expect
!		optional data as in a transfer function.
!	X01200	Changed disconnect processing to use control format
!		instead of the transfer function format.
!	X01210	Fixed memory management bug in Reject processing.
!	X01220	Took out fix of X01210, it was wrong.
!	X01230	Did away with old LLLTM$ clock usage - went to new clock.
!	X01240	Fixed LN2 cleanup on connect failures.
!	X01250	Optimization work.
!	X01260	Fixed error where database was being set up before $mcb call.
!	X01270	Fix for disconnect-received before connect received completion,
!		and fixed context error for $HEADR reference while in MCB mode.
!	X01280	Cleaned up LNK deallocation bug.
!	X01290	Fix of DSR management bug in connect-received processing.
!	X01300	Fixed active-link count for connect failure.
!	x01310  Use DSR space instead of LDB's for connect info
!	X01320	Fixed connect-accept failure to properly clean up link.
!	X01330	Repaired race condition when disconnect received.
!	X01340	Work on bug uncovered by edit #34.
!--
!
! INCLUDE FILES:
!
library 'SCPRM';		! Our parameter and macro definitions
library 'MCB:MCBLIB';
library 'MCB:RSXLIB';
library 'MCB:XPORTX';
library 'MCB:SCSYS';

require 'SCRSX';

!
! TABLE OF CONTENTS:
!
forward routine
    U$DSP: rsx_ucb_scb_iop novalue,		! QIO dispatch

    UFCRCP: mcb_db_ccb_mod novalue,		! Receive complete
    UFCTMO: mcb_db_ccb_mod novalue,		! Timeout
    UFCXCP: mcb_db_ccb_mod novalue;		! Transmit complete

!
! MACROS:
!

!
! EQUATED SYMBOLS:
!

!
! OWN STORAGE:
!

!
! EXTERNAL REFERENCES:
!
external
    $DSPCR,
    $HEADR: ref block field (HDR_FIELDS);

external routine
    SRUDSW: novalue,			! Disconnect-received-wait
    SRUINW: novalue,			! Interrupt-received-wait

    U$ABW: novalue,			! Abort-wait
    U$ACW: novalue,			! Accept-wait
    U$ARQ: novalue,			! * Abort-request-wait *
    U$CLW: novalue,			! Close-wait
    U$CNW: novalue,			! Connect-wait-processing
    U$DSW: novalue,			! Disconnect-wait
    U$GLW: novalue,			! Get-local-node-information-wait
    U$IRQ: novalue,			! * Interrupt-request-wait *
    U$RCW: novalue,			! Receive-wait
    U$RJW: novalue,			! Reject-wait
    U$SNW: novalue,			! Send-wait
    U$XMW: novalue,			! Send-interrupt-wait

    BUFCHK,				! User buffer verification and mapping
    FNDOBJ,				! Find specified object task
    GETLCB,				! Obtain and init LCB
    LCBKIL: novalue,			! Unlink and deallocate LCB
    MBPROC,				! Process mailbox queue
    NOOP: novalue,			! This routine just returns
    QUEAST,				! Queue an AST to RSX
    STXMAP,				! Map from SC codes to RSX User codes
    ULAGET,				! Find available ULA
    ULARD,				! Read specified ULA entry
    ULASET: novalue;			! Set value in specified ULA entry


!++
!
! *** RSX DISPATCH VECTOR ***
!
! This vector is the route via which RSX dispatches
! IOPs to SCX.
!
!--

global bind
    $NSTBL = uplit (
	U$DSP,			! QIO dispatcher
	NOOP,			! I/O cancellation
	NOOP,			! Timeout
	NOOP),			! Power failure

    $ODSP = uplit (
	uplit (U$SNW, U$XMW),					! IOP_XMT
	uplit (U$RCW),						! IOP_RCV
	uplit (U$CNW, U$ACW),					! IOP_CON
	uplit (U$DSW, U$ABW, U$RJW),				! IOP_DSC
	uplit (U$CLW, U$CLW, -1, -1, -1, -1, -1, U$GLW)),	! IOP_CTL

    $IDSP = uplit (
	none_such,		! 
	none_such,		! 
	SRUINW,			! Interrupt received
	SRUDSW);		! Disconnect received


!++
!
! *** MCB DISPATCH VECTOR ***
!
! This vector is the route via which the MCB dispatches
! CCBs to SCX.
!
!--

global bind
    $SCXLL = TABLE$ ($DSPCR, FC_CCP,
	(FC_TIM, UFCTMO),		! UFCTMO - Timeout
	(FC_XCP, UFCXCP),		! UFCXCP - Transmit complete
	(FC_RCP, UFCRCP));		! UFCRCP - Receive complete
%sbttl 'User Interface';
routine U$DSP (UCB, SCB, IOP): RSX_UCB_SCB_IOP novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	UCB	The unit control block
!	SCB	The Status Control Block
!	IOP	The I/O packet
!
! IMPLICIT INPUTS:
!	IOP, SCB, & UCB contents
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    map IOP: ref block field (IOP_FIELDS);
    local
	LUN,
	MODIFIER,
	QFLAG,
	STS,
	COUNT: byte_vector [2],
	SCXDB: ref block field (SCXDB_FIELDS),
	LCB: ref block field (LCB_FIELDS),
	MB_PREV: ref block field (MBP_FIELDS),
	MB: ref block field (MBP_FIELDS),
	STATUS: byte_vector [2],
	TNB: ref block field (TNB_FIELDS);
    bind
	COUNT_WORD = COUNT [0],
	STATUS_WORD = STATUS [0];
    label
	DATA_PROC,
	CONNECT_PROC,
	DISCONNECT_PROC,
	CONTROL_PROC;
    compiletime
	LUN_OFFSET = %fieldexpand (H_LUN, 0) * %upval;

    ! ** Become an MCB process
    MCB$ (.UCB, SCXDB);
    STATUS_WORD = 0;			! Initialize the STATUS vector
    STS = 0;				!   and status flag
    COUNT_WORD = 0;			!   and COUNT variables
    QFLAG = true;			! Assume to queue it to LC_OQUE
    %if %bliss (bliss36)
    %then
	IOP [I_LN2] = (.IOP [I_LN2])^%upval + .$HEADR + LUN_OFFSET - %upval;
    %fi

    LUN = (.IOP [I_LN2] - .$HEADR - LUN_OFFSET + %upval) ^-%upval;
    LCB = TNB = (.(.IOP [I_LN2]) and -2);
    MODIFIER = .IOP [I_MOD] and %o'177';

    STS =
	begin
	if (.IOP [I_FNC] eql IOP_CTL) and (.MODIFIER eql IOP$OPN)	! If OPEN
	then (if .$HEADR [H_NML] neq 0 then IE_PRI else IS_SUC)		! Can't have two
	else (if .$HEADR [H_NML] eql 0 then IE_PRI else IS_SUC)		! But must have one
	end;

    if .STS eql IS_SUC
    then STS =
	begin
	case .IOP [I_FNC] from IOP_LO to IOP_HI of
	    set

	    ! ***************************
	    ! ** Send and Receive Data **
	    ! ***************************
	    [IOP_XMT, IOP_RCV]: DATA_PROC:
		! Expected inputs
		! I_PRM1 = Buffer bias
		! I_PRM2 = Buffer address
		! I_PRM3 = Buffer length

		! ***************************
		! ** U$SND & U$INT & U$RCV **
		! ***************************
		begin
		if (.LCB eql 0) or .LCB [LCF_DSC]
		then leave DATA_PROC with IE_PRI;

		TNB = .LCB [LC_TNB];
		if .MODIFIER eql IOP$INT
		then
		    begin
		    $SCX_ENQUEUE (LCB [LC_NQUE], .IOP);
		    QFLAG = false
		    end;

		IS_SUC
		end;


	    ! ************************
	    ! ** Connect Processing **
	    ! ************************
	    [IOP_CON]: CONNECT_PROC:
		begin
		if .LCB neq 0 then leave CONNECT_PROC with IE_PRI;

		selectone .MODIFIER of
		    set

		    ! ***********
		    ! ** U$ACC **
		    ! ***********
		    [IOP$ACC]:
			! Expected inputs
			! I_PRM1 = Connect block bias
			! I_PRM2 =    "      "   address
			! I_PRM3 =    "      "   length
			! I_PRM4 = Optional output buffer address
			! I_PRM5 =    "       "      "    length

			begin
			local
			    TMP;
			bind
			    CND = IOP [I_PRM2]: ref block field (CND_FIELDS);

			if .IOP [I_PRM3] lss 2
			then leave CONNECT_PROC with IE_BAD;

			if (TMP = BUFCHK (IOP [I_PRM4], IOP [I_PRM3], 16) lss 0)
			then leave CONNECT_PROC with .TMP;

			TNB = .((.$HEADR [H_NML])^%upval + .$HEADR + LUN_OFFSET - %upval);
			MAP$ (.IOP [I_PRM1]);			! Look at connect block
			MB = TNB [TN_MBXQ];			! Scan mailbox
			while (MB_PREV = .MB; MB = .MB [MB_LINK]) neq 0 do
			    begin
			    if .MB eql .CND [CND_TLA] then exitloop
			    end;

			if .MB eql 0 then leave CONNECT_PROC with IE_BAD;

			if not GETLCB (TNB, LCB, .IOP [I_TCB])
			then leave CONNECT_PROC with IE_RSU;
			.IOP [I_LN2] = .LCB;
			LCB [LC_LUN] = .LUN;
			LCB [LC_LLA] = .MB [MB_LLA];
			if (MB_PREV [MB_LINK] = .MB [MB_LINK]) eql 0
			then TNB [TN_MBTL] = .MB_PREV;
			$MCB_RETURN_DSR (MBP_SIZE*bytes_word + .MB [MB_CNT], .MB)
			end;


		    ! ***********
		    ! ** U$CON **
		    ! ***********
		    [IOP$CON]:
			! Expected inputs
			! I_PRM1 = Connect block bias
			! I_PRM2 =    "      "   address
			! I_PRM3 =    "      "   length
			! I_PRM4 = Optional output buffer address
			! I_PRM5 =    "       "      "    length
			! I_PRM6 = Optional input buffer address
			! I_PRM7 =    "       "     "    length

			begin
			local
			    TMP;

			if .IOP [I_PRM3] neq N_RQL
			then leave CONNECT_PROC with IE_BAD;

			if ((TMP = BUFCHK (IOP [I_PRM4], IOP [I_PRM3], 16)) lss 0) or
			   ((TMP = BUFCHK (IOP [I_PRM6], IOP [I_PRM6], 16)) lss 0)
			then leave CONNECT_PROC with .TMP;

			if not GETLCB (TNB, LCB, .IOP [I_TCB])
			then leave CONNECT_PROC with IE_RSU;
			TNB [TN_ACT] = .TNB [TN_ACT] + 1;
			.IOP [I_LN2] = .LCB;
			LCB [LC_LUN] = .LUN
			end;



		    ! **********
		    ! ** OOPS **
		    ! **********
		    [otherwise]: leave CONNECT_PROC with IE_PRI;

		    tes;

		leave CONNECT_PROC with IS_SUC
		end;
	    ! ***************************
	    ! ** Disconnect Processing **
	    ! ***************************
	    [IOP_DSC]: DISCONNECT_PROC:
		! Expected inputs
		! I_PRM1 =    "      "   address
		! I_PRM2 =    "      "   length
		! I_PRM4 = Optional output buffer address (REJ only)
		! I_PRM5 =    "       "      "    length  ( "   "  )

		begin
		local
		    TMP;

		IOP [I_PRM5] = .IOP [I_PRM4];		! Copy PRM3 & PRM4
		IOP [I_PRM4] = .IOP [I_PRM3];		!  to make room for
							! expanded address
		if (TMP = BUFCHK (IOP [I_PRM1], IOP [I_PRM1], 0)) lss 0
		then leave DISCONNECT_PROC with TMP;

		selectone .MODIFIER of
		    set
	
		    ! *******************
		    ! ** U$ABO & U$DSC **
		    ! *******************
		    [IOP$ABO, IOP$DSC]:
			! Expected inputs
			! I_PRM1 = Optional Data bias
			! I_PRM2 =    "      "   address
			! I_PRM3 =    "      "   length

			begin
			local
			    TMP;
			if (.LCB eql 0) or .LCB [LCF_DSC]
			then leave DISCONNECT_PROC with IE_PRI;
			TNB = .LCB [LC_TNB];
			if .IOP [I_PRM3] gtr 16
			then leave DISCONNECT_PROC with IE_BAD;
			LCB [LCF_DSC] = true;		! Abort/Disc started
			if .MODIFIER eql IOP$ABO
			then
			    begin
			    $SCX_STACK (LCB [LC_NQUE], .IOP);
			    QFLAG = false
			    end
			end;


		    ! ***********
		    ! ** U$REJ **
		    ! ***********
		    [IOP$REJ]:
			! Expected inputs
			! I_PRM1 = Connect block bias
			! I_PRM2 =    "      "   address
			! I_PRM3 =    "      "   length
			! I_PRM4 = Optional output buffer address
			! I_PRM5 =    "       "      "    length

			begin
			local
			    TMP;
			bind
			    CND = IOP [I_PRM2]: ref block field (CND_FIELDS);

			LCB = 0;
			if .LUN neq .$HEADR [H_NML]
			then leave DISCONNECT_PROC with IE_PRI;

			if .IOP [I_PRM3] lss 2
			then leave DISCONNECT_PROC with IE_BAD;

			if (TMP = BUFCHK (IOP [I_PRM4], IOP [I_PRM3], 16)) lss 0
			then leave DISCONNECT_PROC with .TMP;
			MAP$ (.IOP [I_PRM1]);			! Look at connect block
			MB = TNB [TN_MBXQ];			! Scan mailbox
			while (MB_PREV = .MB; MB = .MB [MB_LINK]) neq 0 do
			    begin
			    if .MB eql .CND [CND_TLA] then exitloop
			    end;

			if .MB eql 0 then leave DISCONNECT_PROC with IE_BAD;

			IOP [I_PRM6] = .MB [MB_LLA];		! Remember LLA
			if (MB_PREV [MB_LINK] = .MB [MB_LINK]) eql 0
			then TNB [TN_MBTL] = .MB_PREV;
			$MCB_RETURN_DSR (MBP_SIZE*bytes_word + .MB [MB_CNT], .MB);
			$SCX_ENQUEUE (TNB [TN_OQUE], .IOP);
			QFLAG = false
			end;


		    ! **********
		    ! ** OOPS **
		    ! **********
		    [otherwise]: leave DISCONNECT_PROC with IE_PRI;

		    tes;

		leave DISCONNECT_PROC with IS_SUC
		end;
	    ! *************
	    ! ** Control **
	    ! *************
	    [IOP_CTL]: CONTROL_PROC:
		begin
		LCB = 0;
		if .MODIFIER neq IOP$OPN
		then
		    begin
		    if (.LUN neq .$HEADR [H_NML]) or (.TNB eql 0)
		    then leave CONTROL_PROC with IE_PRI
		    end;

		selectone .MODIFIER of
		    set

		    ! ***********
		    ! ** U$OPN **
		    ! ***********
		    [IOP$OPN]:
			! Expected inputs
			! I_PRM1 = Maximum allowed logical links

			begin
			! Get TNB block of memory from DSR
			if not $MCB_GET_DSR (TNB_SIZE*bytes_word, TNB)
			then leave CONTROL_PROC with IE_RSU;

			! ** Zero and initialize block
			ch$fill(0, SCXDB_SIZE*bytes_word, .TNB);
			$HEADR [H_NML] = TNB [TN_LUN] =	.LUN;	! Record LUN #
			TNB [TN_LINK] = .SCXDB [SCX_TNB];
			SCXDB [SCX_TNB] = .TNB;
			.(IOP [I_LN2]) = .TNB;		! Set TNB addr in LN2
			TNB [TN_TASK] = .IOP [I_TCB];	! TCB address
			TNB [TN_MAX] = .IOP [I_PRM1];	! Max # of links
			TNB [TN_OQTL] = TNB [TN_OQHD];	! Init queue
			TNB [TN_MBTL] = TNB [TN_MBHD];	!   headers
			TNB [TN_IQTL] = TNB [TN_IQHD];	!     ...

			! ** Now move stuff from general delivery mailbox
			! **   to the task's mailbox
			MB = SCXDB [SCX_MBHD];					! Point to head of list
			while (MB_PREV = .MB; MB = .MB [MB_LINK]) neq 0 do	! Go thru whole list
			    begin
			    if .MB [MB_TASK] eql .TNB [TN_TASK]	! If for this task
			    then
				begin
				if (.MB [MB_FNC] eql MB$CON) and
				((.TNB [TN_MAX] neq 0) and (.TNB [TN_ACT] geq .TNB [TN_MAX]))
				then
				    MB [MB_TIME] = 1	! Too many links, a short timeout
				else
				    begin
				    if .MB [MB_FNC] eql MB$CON
				    then TNB [TN_ACT] = .TNB [TN_ACT] + 1;
				    if (MB_PREV [MB_LINK] = .MB [MB_LINK]) eql 0
				    then SCXDB [SCX_MBTL] = .MB_PREV;
				    $SCX_ENQUEUE (TNB [TN_MBXQ], .MB);
				    MB = .MB_PREV
				    end
				end
			    end;

			STATUS [0] = .STS;
			RSX$ (.SCXDB);
			IOFIN$ (.UCB, .IOP, .STATUS_WORD, .COUNT_WORD);
			return
			end;


		    ! ***********
		    ! ** U$CLS **
		    ! ***********
		    [IOP$CLS]:
			begin
			$SCX_STACK (TNB [TN_OQUE], .IOP);	! Do it soon
			TNB [TNF_CLS] = true;			! We started it
			QFLAG = false;
			leave CONTROL_PROC with IS_SUC
			end;


		    ! ***********
		    ! ** U$SPA **
		    ! ***********
		    [IOP$SPA]:
			! Expected inputs
			! I_PRM1 = AST address

			begin
			TNB [TN_SPA] = .IOP [I_PRM1];

			! Count number of things in queue
			MB = TNB [TN_MBXQ];
			while (MB = .MB [MB_LINK]) neq 0 do
			    begin
			    if .MB [MB_FNC] neq MB$CNP
			    then COUNT [0] = .COUNT [0] + 1
			    end;

			RSX$ (.SCXDB);
			IOFIN$ (.UCB, .IOP, IS_SUC, .COUNT_WORD);
			return
			end;

		    ! ***********
		    ! ** U$GND **
		    ! ***********
		    [IOP$GND1, IOP$GND2, IOP$GND3]:
			! Expected inputs
			! I_PRM1 = Buffer address
			! I_PRM2 = Buffer length
			! I_PRM3 = Mask parameter
			! I_PRM4 = (PRM3 will be copied to here)

			begin
			local
			    FNC,
			    SELEKT;

			IOP [I_PRM4] = .IOP [I_PRM3];	! Copy mask parameter
			if .MODIFIER neq IOP$GND2	! Check buffer, if must
			then
			    begin
			    local TMP;
			    if (TMP = BUFCHK (IOP [I_PRM1], IOP [I_PRM1], 0)) lss 0
			    then leave CONTROL_PROC with .TMP
			    end;

			! Now scan the mailbox queue
			MB = TNB [TN_MBXQ];
			while (MB_PREV = .MB; MB = .MB [MB_LINK]) neq 0 do
			    begin
			    SELEKT = true;			! Assume success
			    if (FNC = .MB [MB_FNC]) eql MB$CNP	! Pending connects are invisible to caller
			    then SELEKT = false			! No good
			    else
				begin
				local
				    TYPE_MASK,
				    LUN_MASK;

				if .MODIFIER eql IOP$GND1	! If normal GND
				then exitloop;			! Got it !

				TYPE_MASK = .IOP [$sub_field (I_PRM4, LO_BYTE)];
				LUN_MASK = .IOP [$sub_field (I_PRM4, HI_BYTE)];

				if (
				    ((.TYPE_MASK neq 0) and (.TYPE_MASK neq .FNC)) or
				    ((.LUN_MASK neq 0) and (.LUN_MASK neq .MB [MB_LUN])))
				then SELEKT = false;		! Didn't make it
				end;

			    if .SELEKT then exitloop
			    end;

			if not .SELEKT then leave CONTROL_PROC with IE_NDA;

			if .MODIFIER eql IOP$GND2	! Length and type only
			then
			    begin
			    STS = IS_SUC;
			    STATUS [1] = .FNC;
			    COUNT [0] = .MB [MB_CNT]
			    end
			else
			    begin
			    COUNT [0] = min (.MB [MB_CNT], .IOP [I_PRM3]);	! Determine size of xfer
			    COUNT [1] = .MB [MB_LUN];				! Say which LUN (zero for connect)
			    STS = (if .COUNT [0] geq .MB [MB_CNT] then IS_SUC else IE_DAO);
			    STATUS [1] = .FNC;					! Tell user what it is
			    MAP$ (.IOP [I_PRM1]);				! Map user buffer
			    ch$move (.COUNT [0], byt$ptr (MB [MB_DATA]), byt$ptr (.IOP [I_PRM2]));

			    if .FNC eql MB$CON
			    then
				MB [MB_FNC] = MB$CNP		! Logically dequeue connect
			    else
				begin
				if (MB_PREV [MB_LINK] = .MB [MB_LINK]) eql 0	! Physically dequeue MB
				then TNB [TN_MBTL] = .MB_PREV;			!   entry, and
				LCB = ULARD (.MB [MB_ULA]);	! Get ULA address
				$MCB_RETURN_DSR (MBP_SIZE*bytes_word + .MB [MB_CNT], .MB);	!   dealc  memory

				if .FNC eql MB$INT
				then U$IRQ (.LCB)
				else
				    begin
					LCB [LCF_DSC] = true;
					U$ARQ( .LCB)
				    end
				end
			    end;

			STATUS [0] = .STS;
			RSX$ (.SCXDB);
			IOFIN$ (.UCB, .IOP, .STATUS_WORD, .COUNT_WORD);
			return
			end;

		    ! ***********
		    ! ** U$GLN **
		    ! ***********
		    [IOP$GLN]:
			! Expected inputs
			! I_PRM1 = Buffer address
			! I_PRM2 = Buffer length
			! I_PRM3 = Option selector

			begin
			local
			    TMP;

			IOP [I_PRM4] = .IOP [I_PRM3];
			if (.IOP [I_PRM2] eql 0) or 
			   (TMP = BUFCHK (IOP [I_PRM1], IOP [I_PRM1], 0)) lss 0
			    then leave CONTROL_PROC with .TMP;

			$SCX_ENQUEUE (TNB [TN_OQUE], .IOP);
			QFLAG = false
			end;


		    ! **********
		    ! ** OOPS **
		    ! **********
		    [otherwise]: leave CONTROL_PROC with IE_PRI;

		    tes
		end;
	    ! **********
	    ! ** Zonk **
	    ! **********
	    [inrange, outrange]:
		begin
		selectoneu .IOP [I_FNC] of

		    set

		    ! ***********
		    ! ** CLOSE **
		    ! ***********
		    [IOP$CLS]:
			begin
			if (.LUN neq .$HEADR [H_NML]) or (.TNB [TNF_CLS]) then return
			IOP [I_FNC] = IOP_CTL;		! Change codes awhile
			IOP [I_MOD] = IOP$CLO;
			$SCX_STACK (TNB [TN_OQUE], .IOP);
			QFLAG = false;
			IS_SUC
			end;


		    ! ********************
		    ! ** DISMOUNT, OR ? **
		    ! ********************
		    [otherwise]: IE_PRI;

		    tes
		end
	    tes
	end;
    STATUS [0] = .STS;
    if .STS geq 0
    then
	begin
	if .QFLAG then $SCX_ENQUEUE (LCB [LC_OQUE], .IOP);
	if .TNB neq 0
	    then $SCX_DISPATCH_OUTPUT (TNB [TN_OQUE], .TNB);
	if .LCB neq 0
	then
	    begin
	    $SCX_DISPATCH_OUTPUT (LCB [LC_NQUE], .LCB);
	    $SCX_DISPATCH_OUTPUT (LCB [LC_OQUE], .LCB)
	    end;
	RSX$ (.SCXDB)
	end
    else
	begin
	RSX$ (.SCXDB);
	IOFIN$ (.UCB, .IOP, .STATUS_WORD, .COUNT_WORD)
	end
    end;
%sbttl 'Receive-Complete from Session Control';
routine UFCRCP (SCXDB, CCB, FCM): mcb_db_ccb_mod novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by receipt of a receive
!	complete from Session Control.
!
! FORMAL PARAMETERS:
!	CCB	CCB to pass to handler routine
!	FCM	Function code modifier
!
! IMPLICIT INPUTS:
!	CCB contents
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    $scx_get_data_base (SCXDB);
    map CCB: ref block field (C_FIELDS);
    local
	LCB: ref block field (LCB_FIELDS),
	TNB: ref block field (TNB_FIELDS),
	MB: ref block field (MBP_FIELDS);

    case .FCM^-1 from 0 to 3 of
	set

	! ************
	! ** SRUCNR **
	! ************
	[S$CNR^-1]:
	    begin
	    local
		STS,
		MBD: ref block field (CND_FIELDS),
		TCB: ref block field (TCB_FIELDS);
	    bind
		CB = CCB [C_ADDR]: ref block field (CB_FIELDS);
	    literal
		CB_L1 = (%fieldexpand (CB_SFMT, 0) - %fieldexpand (CB_DFMT, 0)) * bytes_word,
		CB_L2 = (%fieldexpand (CB_ENDD, 0) - %fieldexpand (CB_SFMT, 0)) * bytes_word;

	    MAP$ (.CCB [C_BIAS]);		! Look at connect block
	    if not FNDOBJ (CB [CB_DFMT], TCB)
	    then
		begin
		$SCX_DO_RCE (.CCB, S_EURO);
		return
		end;
	    if not $MCB_GET_DSR (MBP_SIZE*bytes_word + CND_SIZE*bytes_word, MB)
	    then
		begin
		$SCX_DO_RCE (.CCB, S_ERES);
		return
		end;

	    MBD = MB [MB_DATA];			! Set cb base
	    MBD [CND_TLA] = .MB;		! Temporary LLA
	    MBD [CND_SEG] = 0;
	    ch$move (CB_L1, byt$ptr (CB [CB_DFMT]), byt$ptr (MBD [CND_DFMT]));
	    ch$move (6, byt$ptr (CB [CB_NODE]), byt$ptr (MBD [CND_NODE]));
	    ch$move (CB_L2, byt$ptr (CB [CB_SFMT]), byt$ptr (MBD [CND_SFMT]));
	    MB [MB_CNT] = CND_SIZE*bytes_word;
	    MB [MB_FNC] = MB$CON;
	    MB [MB_FLG] = 0;
	    MB [MB_LLA] = .CCB [C_LIX];
	    MB [MB_LUN] = 0;
	    MB [MB_TIME] = .CCB [C_PRM2] - 1;
	    MB [MB_TASK] = .TCB;

	    TNB = .SCXDB [SCX_TNB];
	    while .TNB neq 0 do
		begin
		if .TNB [TN_TASK] eql .TCB
		then exitloop
		else TNB = .TNB [TN_LINK]
		end;

	    MB [MB_TNB] = .TNB;			! Remember where (if) the TNB
	    STS = S_SSUC;
	    if .TNB neq 0
	    then
		begin
		if (.TNB [TN_MAX] neq 0) and (.TNB [TN_ACT] geq .TNB [TN_MAX])
		then
		    begin
		    $MCB_RETURN_DSR (MBP_SIZE*bytes_word + .MB [MB_CNT], .MB);
		    STS =  S_EOTB
		    end
		else
		    begin
		    TNB [TN_ACT] = .TNB [TN_ACT] + 1;
		    $SCX_ENQUEUE (TNB [TN_MBXQ], .MB);
		    QUEAST (.TNB);
		    end
		end
	    else
		begin
		if not TSKRT$ (.TCB, 0) then MB [MBF_REQ] = true;
		$SCX_ENQUEUE (SCXDB [SCX_MBX], .MB);
		SCXDB [SCXF_KLOK] = true
		end;

	    $SCX_DO_RCE (.CCB, .STS);
	    return
	    end;


	! *********************
	! ** SRUINT & SRUDSR **
	! *********************
	[S$INT^-1, S$DSR^-1]:
	    begin
	    if (LCB = ULARD (.CCB [$sub_field (C_PRM1, LO_BYTE)])) eql 0
	    then
		begin
		$SCX_DO_RCE (.CCB);
		return
		end;

	    TNB = .LCB [LC_TNB];
	    $SCX_ENQUEUE (TNB [TN_IQUE], .CCB);
	    $SCX_DISPATCH_INPUT (TNB [TN_IQUE])
	    end;

	! ************
	! ** SRUERR **
	! ************
	[inrange, outrange]: SIGNAL_STOP (SCX$_ISC, .CCB);

	tes;
    end;
%sbttl 'Timeout or Initialization From Comm/Exec';
routine UFCTMO (SCXDB, CCB, FCM): mcb_db_ccb_mod novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by some event noted by
!	the Comm/Exec.
!
! FORMAL PARAMETERS:
!	CCB	CCB to pass to handler routine
!	FCM	Function code modifier
!
! IMPLICIT INPUTS:
!	CCB Contents
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    $scx_get_data_base (SCXDB);
    map CCB: ref block field (C_FIELDS);

    case .FCM^-1 from 0 to 3 of
	set

	! ************
	! ** CXULTM **
	! ************
	[1]:
	    begin
	    local
		LCB: ref block field (LCB_FIELDS),
		TNB: ref block field (TNB_FIELDS);

	    SCXDB [SCX_TICK] = .SCXDB [SCX_TICK] + 1;	! Restart clock
	    SCXDB [SCXF_SUCC] = true;
	    MBPROC (SCXDB [SCX_MBX]);		! Process Gen-Deliv connects

	    TNB = .SCXDB [SCX_TNB];		! Point to TNB list
	    while .TNB neq 0 do			! Look at all TNBs
		begin
		MBPROC (TNB [TN_MBXQ]);			! Process TNB connects

		if .SCXDB [SCXF_SUCC] and .TNB [TNF_RWT] ! Anyone wanting resources ?
		then
		    begin				! Yes
		    TNB [TNF_RWT] = false;		! Turn off request flag
		    $SCX_DISPATCH_OUTPUT (TNB [TN_OQUE], .TNB);	! Do task level stuff

		    LCB = TNB [TN_LCB];			! Now, step thru LCBs
		    while (LCB = .LCB [LC_LINK]) neq 0 do
			begin
			if .LCB [LCF_NRES]		! Interrupt request ?
			then
			    begin
			    LCB [LCF_NRES] = false;
			    U$IRQ (.LCB)
			    end;
			if .LCB [LCF_ARES]		! Abort request ?
			then
			    begin
			    LCB [LCF_ARES] = false;
			    LCB [LCF_DSC] = true;
			    U$ARQ (.LCB)
			    end;

			! Pump the interrupt, input, and output queues
			if .SCXDB [SCXF_SUCC]
			then
			    begin
			    $SCX_DISPATCH_OUTPUT (LCB [LC_NQHD], .LCB);
			    $SCX_DISPATCH_OUTPUT (LCB [LC_OQHD], .LCB);
			    $SCX_DISPATCH_INPUT (TNB [TN_IQHD])
			    end
			end
		    end;

		if .TNB [TNF_CLS]		! Net being closed ?
		then
		    begin			! Yes
		    local
			NXT_TNB;

		    NXT_TNB = .TNB [TN_LINK];
		    U$CLW (.TNB);		! Do the close
		    TNB = .NXT_TNB
		    end
		else
		    TNB = .TNB [TN_LINK]
		end
	    end;



	! ************
	! ** CXUPWF **
	! ************
	[2]: return;



	! ************
	! ** CXUPIN **
	! ************
	[3]:
	    begin
	    bind SC =
		%if %bliss (bliss36)
		%then %rad50_10 'SC'
		%else %rad50_11 'SC'
		%fi;

	    if not PDVID$ (SC, SCXDB [SCX_SCPIX])	! Set SC PIX
	    then SIGNAL_STOP (SCX$_NSC);
	    RSX$ (.SCXDB, SCXDB [SCX_UCB]);		! Record UCB address
	    MCB$ (.SCXDB [SCX_UCB]);			! Back to MCB
	    $MCB_ENABLE_LONG_TIMER ();			! Start clock
	    SCXDB [SCX_TICK] = 1			! ...
	    end;



	! ************
	! ** CXUERR **
	! ************
	[inrange, outrange]: SIGNAL_STOP (SCX$_ISC);
	tes
    end;
%sbttl 'Transmit-Complete From Session Control';
routine UFCXCP (SCXDB, CCB, FCM): mcb_db_ccb_mod novalue =

!++
! FUNCTIONAL DESCRIPTION:
!	This routine is activated by receipt of an xmit
!	complete from Session Control.
!
! FORMAL PARAMETERS:
!	CCB	CCB to pass to handler routine
!	FCM	Function code modifier
!
! IMPLICIT INPUTS:
!	CCB contents
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    $scx_get_data_base (SCXDB);
    map CCB: ref block field (C_FIELDS);
    local
	COUNT,
	IOP: ref block field (IOP_FIELDS);
    compiletime
	LUN_OFFSET = %fieldexpand (H_LUN, 0) * %upval;
    label
	ABO_DIS;

    COUNT = 0;
    IOP = .CCB [C_PRM5];		! Set up address of IOP

    case .FCM^-1 from 0 to 10 of
	set

	! ******************************
	! ** SXUABO & SXUDIS & SXUACC **
	! ******************************
	[S$ABO^-1, S$DIS^-1, S$ACC^-1]: ABO_DIS:
	    begin
	    local
		LCB: ref block field (LCB_FIELDS),
		TNB: ref block field (TNB_FIELDS),
		LIOP: ref block field (IOP_FIELDS),
		MB: ref block field (MBP_FIELDS),
		MB_PREV: ref block field (MBP_FIELDS);

	    LCB = ULARD (.CCB [$sub_field (C_PRM1, LO_BYTE)]);
	    TNB = .LCB [LC_TNB];

	    if .FCM eql S$ACC
	    then
		begin
		COUNT = .CCB [C_CNT];
		leave ABO_DIS
		end;

	    MB = TNB [TN_MBXQ];			! Flush mailbox for this link
	    while (MB_PREV = .MB; MB = .MB [MB_LINK]) neq 0 do
		begin
		if .MB [MB_ULA] eql .CCB [$sub_field (C_PRM1, LO_BYTE)]
		then
		    begin
		    if (MB_PREV [MB_LINK] = .MB [MB_LINK]) eql 0
		    then TNB [TN_MBTL] = .MB_PREV;
		    $MCB_RETURN_DSR (MBP_SIZE*bytes_word + .MB [MB_CNT], .MB)
		    end
		end;

	    ! Flush the output queues
	    while $SCX_DEQUEUE (LCB [LC_OQUE], LIOP) do IOFIN$ (.SCXDB [SCX_UCB], .LIOP, IE_ABO, 0);
	    while $SCX_DEQUEUE (LCB [LC_NQUE], LIOP) do IOFIN$ (.SCXDB [SCX_UCB], .LIOP, IE_ABO, 0);

	    ! Clear user's LN2 word, clear the ULA, and get rid of link
	    ((.LCB [LC_LUN] ^%upval) + .$HEADR + LUN_OFFSET - %upval) = 0;
	    ULASET (.LCB [LC_ULA], 0);
	    TNB [TN_ACT] = .TNB [TN_ACT] - 1;
	    LCBKIL (.LCB)
	    end;



	! ************
	! ** SXUCON **
	! ************
	[S$CON^-1]:
	    begin
	    local
		LCB: ref block field (LCB_FIELDS);

	    LCB = (.(.IOP [I_LN2]) and -2);
	    if .CCB [C_STS] neq S_SSUC
	    then
		begin
		bind TNB = .LCB [LC_TNB]: block field (TNB_FIELDS);
		TNB [TN_ACT] = .TNB [TN_ACT] - 1;
		LCBKIL (.LCB);		! Deallocate LCB and ULA and LN2
		ULASET (.CCB [$sub_field (C_PRM1, LO_BYTE)], 0);
		(.IOP [I_LN2]) = 0;
		end
	    else
		LCB [LC_LLA] = .CCB [C_LIX];

	    IOFIN$ (.IOP [I_UCB], .IOP, STXMAP (.CCB [C_STS]), .CCB [C_PRM4]);
	    $MCB_RETURN_DSR( DSR_CONNECT_BLOCK_SIZE, .CCB[C_ADDR]);
	    CCBRT$( .CCB);
	    return
	    end;



	! ************
	! ** SXUREJ **
	! ************
	[S$REJ^-1]:
	    begin
	    bind
		TNB = CCB [C_PRM4]: ref block field (TNB_FIELDS);

	    if .TNB neq 0 then TNB [TN_ACT] = .TNB [TN_ACT] - 1;
	    COUNT = .CCB [C_CNT]
	    end;



	! ********************************************
	! ** SXUGLN, SXUIRQ, SXUMRQ, SXUSND, SXUSNI **
	! ********************************************
	[inrange]: COUNT = .CCB [C_CNT];



	! ************
	! ** SXUERR **
	! ************
	[outrange]: SIGNAL_STOP (SCX$_ISC, .CCB);

	tes;

    if .IOP neq 0 then IOFIN$ (.IOP [I_UCB], .IOP, STXMAP (.CCB [C_STS]), .COUNT);
    CCBRT$ (.CCB)
    end;

end
eludom