Google
 

Trailing-Edge - PDP-10 Archives - BB-P363B-SM_1985 - mcb/drivers/dmc.b16
There are no other files named dmc.b16 in the archive.
module DMC (	! Driver for DMC11
		ident = 'X04690',
		language (bliss16)
		) =
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: MCB DMC Driver
!
! ABSTRACT:
!
!	This is the DLC process which services the DMC11 synchronous
!	line interface.
!
! ENVIRONMENT: MCB V3.0
!
! AUTHOR: Alan D. Peckham	CREATION DATE: 7-Apr-80
!
! MODIFIED BY:
!
!	Maynard, 10-Feb-78: Version 1
!	Maynard, 29-Feb-79: Version 3
!	Alan D. Peckham, 7-Apr-80: Version 4
! 01	-  Update to operate under MCB V3.0
! 02	- Check DS.MC properly in GET_PORT.
!	  Clear DS.MC after DMC is set up in CTLSTR.
!	  Make initialization handle D.XBFC properly.
!	  Correct queue address calculation in CTLSTP.
!	  Receive buffer lengths come from .RDBSZ, not C.CNT!
!	  Save the CTL/STP CCB while flushing the queues.
! 03	- Do not copy transmit buffer if single segment message.
! 04	- Change call JMPs to CALLRs and add macro for debugging.
! 05	- Seperate DLC and DDM dispatch tables to avoid C.MOD bug.
! 06	- Put routines in alphabetical order.
!	  Eliminate CALLRs.
!	  Fix bit test in CTLSTP.
!	  Master clear DMC on fatal errors in CONTROL_OUT.
!	  Turn off output interrupts during interrupt service.
!	  Ignore output interrupt while DMC inactive.
! 07	- Insert SIGNALs for empty queues.
! 08	- Rewrite in BLISS and update for MCB V3.1.
! 09	- Add internal event logging.
! 10	- Map the data base and add Network Management code.
! 11	- Add Network Management event logging.
! 12	- Clean up current Network Management code.
! 13	- Signal on event logging buffer too small.
! 14	- Add long timer control.
! 15	- Compensate for UNWIND bug in condition handling.
! 16	- Process multiple output transfers during output interrupt.
!	  Allow master clear to fail.
! 17	- Fix interrupt vector assignment bug in DMCNMX.
!	  Allow number of receive buffers to be specified in CETAB.
! 18	- Temporarily remove RQI test in GET_PORT to allow lost
!	  RDYI recovery.
! 19	- Identify line and circuit to NMX in DMCNMX.
! 20	- Move BASE-IN and CONTROL-IN in test loop in SET_PORT.
! 21	- Argh! Create common interrupt service routine which
!	  processes both input and output requests.
! 22	- Streamline linkages.
!	  Do not SYNCH unless interrupt enable is on.
! 23	- Do not reset IEO during interrupt handling (no-no on DMR).
!	  Rewrite port handling code.
! 24	- Rework HALT command handling.
!	  Reset certain variables on CTL/STR.
! 25	- Flush transmits before waiting for the cows to come home in CTLSTP.
! 26	- Hunting for bugs. Rework timer code.
!	  Rework CCB control function handling.
! 27	- Experimental bug fixes to look for shutdown bug.
! 29	- Correct event logging bugs.
! 30	- Add code to record program flow of shutdown.
!	  Verify message lengths by examination in RECEIVE_OUT.
!	  Verify receive buffer addresses on RECEIVE_OUT.
! 31	- Remove message length verification.
!	  Add transmit buffer address verification in TRANSMIT_OUT.
! 32	- Fix transmit buffer address verification in TRANSMIT_OUT.
!	  Optimize some generated code.
! 33	- Close window in CTL/STP/STR which allowed CTL deadlock.
! 34	- Do not set function code during port load in SERVICE_DMC
!	  in order to avoid clearing RDI.
! 35	- Remove DLC interface.
! 36	- Add controller loopback support.
! 37	- Add 96+5/13 event for NM line attention.
! 38	- Distinguish between overrun due to no buffer available
!	  and due to transmission threshold.
! 39	- Finish DLC interface removal.
!	  Add parameter to SHOW LINE CHARACTERISTICS.
! 40	- Do NOT send state when there is no circuit owner.
! 41	- Do not set half duplex when in maintenance mode.
! 42	- Add DMR line/device name display and support for DMR process.
! 43	- Adapt to using new UNIBUS address conversion routines.
!	  Adjust buffer address checking for DMC bug.
! 44	- Eliminate extra OFF notification in CURRENT_STATUS.
!	  Optimize CHECK_BA.
! 45	- Add device and performance error counter support.
!	  Fix control CCB handling - CTLNXT had not been updated.
! 46	- Do not declare interrupt lost in TIMLTM if synchronization
!	  in progress.
! 47	- Turn off FLUSHING_DMC when DMC shuts down.
! 48	- More work on non-existent device notification.
! 49	- Cut down on extraneous functionality to save space.
! 50	- Set threshold errors to display circuit counters.
! 51	- Handle counters by copying when zeroed and using differences
!	  for reporting.
! 52	- Add "counters zeroed" event.
! 53	- Remove "counters zeroed" event.
! 54	- Update to NM V3.0.0 .
! 55	- Set default DMC receive buffers to 7 in DMCINI.
!	  Add MAXIMUM RECEIVE BUFFERS parameter support in DMCNMX.
! 56	- Schedule DC_CONTROL_IN after ZERO_x_COUNTERS in SET_INPUT_REQUEST
!	  if DMC is only DD_HALTED.
! 57	- Disable TRANSMIT_THRESHOLD_ERROR notification on O_RUN.
!	  Do not force HALT state for ZERO_COUNTERS.
! 58	- Support new system specific line counters.
! 59	- De-commit half-duplex.
! 60	- Poke our DEVTYP into any MOP message requiring it.
! 61	- Handle change in NMX interface in DMCINI.
! 62	- Re-enable half-duplex code.
! 63	- Disable time out for first transmit.  This is because protocol
!	  may not have been established yet.
! 64	- Make sure flag is cleared at appropriate places.
! 65	- Fix display of DMC counter #1021 in DMCNMX.
! 66	- Modify CTLSTR to return the CIRCUIT COST to XPT in C_PRM1.
! 67	- We got the meaning of the timer bit in a control-in wrong -
!	  the 1 and 3 second timers were reversed.
! 68	- Fix TIMLTM to do a transmit timeout on maintenance messages.
!
!	Dennis Brannon, 23-Sep-83: Version 4 autopatch tape 7
! 69	- Fix CONTROL_OUT to reinitialize DMR/DMC after it turns the
!	  circuit off when a DDCMP START is received.
!--
!
! INCLUDE FILES:
!

library 'MCBLIB';

library 'XPORTX';

$SHOW (NONE)
require 'DLLLIB';

library 'NMXLIB';

library 'DMCDAT';

!
! TABLE OF CONTENTS:
!

linkage
    CBIT_LINKAGE = jsr (register = 4) : clearstack valuecbit,
    DMC_CCB_DB = jsr (register = 4, register = 5) : nopreserve (4),
    DMC_CCB_DB_STS = jsr (register = 4, register = 5, register = 0) :
	nopreserve (4),
    DMC_DB = jsr (register = 5) : nopreserve (0, 1, 2, 3, 4),
    DMC_DB_BIAS_ADDR = jsr (register = 5, register = 2, register = 3) :
	nopreserve (0, 2, 3),
    DMC_DB_CCB = jsr (register = 5, register = 4) : nopreserve (4),
    DMC_DB_ERR = jsr (register = 5, register = 3) : nopreserve (3, 4),
    DMC_DB_LOW_HIGH = jsr (register = 5) : nopreserve (2, 3, 4),
    DMC_REG = jsr (register = 1) : nopreserve (1),
    DMC_DB_STS = jsr (register = 5, register = 3) : nopreserve (3, 4);

forward routine
    BASE_IN : DMC_DB_LOW_HIGH novalue,
    CHECK_BA : DMC_DB_BIAS_ADDR,
    CONTROL_OUT : DMC_DB_ERR novalue,
    CTLNXT : LINKAGE_DB novalue,
    CTLSTP : MCB_DB_CCB novalue,
    CTLSTR : MCB_DB_CCB novalue,
    CURRENT_STATUS : LINKAGE_DB,
    DEVICE_SHUT_DOWN : LINKAGE_DB novalue,
    DLLINI : DMC_DB_CCB novalue,
    DLLMOP : DMC_DB_CCB novalue,
    DLLSTP : DMC_DB_CCB novalue,
    DMC_INTERRUPT : MCB_INTERRUPT novalue,
    DMC_SYNCHRONIZE : MCB_DB novalue,
    DMCCTL : MCB_DB_CCB_MOD novalue,
    DMCDB : CALL$ novalue,
    DMCRCE : MCB_DB_CCB novalue,
    DMCTIM : MCB_DB_MOD novalue,
    DMCXME : MCB_DB_CCB_MOD novalue,
    FLUSH_QUEUES : DMC_DB novalue,
    MASTER_CLEAR : LINKAGE_DB,
    OUTPUT_TRANSFER : LINKAGE_DB novalue,
    PARAMETER_DEVICE_REGISTERS : DMC_REG novalue,
    QUEUE_TRANSMIT : DMC_DB_CCB novalue,
    RECEIVE_IN : DMC_DB_LOW_HIGH novalue,
    RECEIVE_OUT : DMC_DB_LOW_HIGH novalue,
    RESET_TIMER : LINKAGE_DB novalue,
    SCHEDULE_PERSISTENT_AST : DMC_CCB_DB novalue,
    SCHEDULE_RECEIVE : DMC_CCB_DB novalue,
    SCHEDULE_STATE_AST : DMC_CCB_DB novalue,
    SCHEDULE_TRANSIENT_AST : DMC_CCB_DB novalue,
    SCHEDULE_TRANSMIT : DMC_CCB_DB_STS novalue,
    SEND_PERSISTENT_ERROR : DMC_DB_STS novalue,
    SEND_STATE : DMC_DB novalue,
    SEND_TRANSIENT_ERROR : DMC_DB_STS novalue,
    SERVICE_DMC : MCB_DB novalue,
    SET_INPUT_REQUEST : LINKAGE_DB novalue,
    SET_PORT : LINKAGE_DB novalue,
    TIMCCB : MCB_DB_CCB novalue,
    TIMLTM : MCB_DB novalue,
    TIMPIN : MCB_DB novalue,
    TIMPWF : MCB_DB novalue,
    TIMRDB : MCB_DB_CCB novalue,
    TRANSMIT_IN : DMC_DB_LOW_HIGH novalue,
    TRANSMIT_OUT : DMC_DB_LOW_HIGH novalue,
    ZERO_CIRCUIT_COUNTERS : LINKAGE_DB novalue,
    ZERO_LINE_COUNTERS : LINKAGE_DB novalue;

global bind routine
    DMCIN = DMC_INTERRUPT : MCB_INTERRUPT novalue,
    DMCOUT = DMC_INTERRUPT : MCB_INTERRUPT novalue;

!
! MACROS:
!

macro
    CHANGE_PROTOCOL_STATE (DUMMY, STATE, REASON) =
	begin
	literal LCL = 0, RMT = 1;
	$NM_DLL_EVENT (REASON, .DB [D_CIRCUIT_ENTITY],
	    PARAMETER_C_1 (0, DB [D_PROTOCOL_STATE]),
	    DB [D_PROTOCOL_STATE] = STATE,
	    PARAMETER_C_1 (1, DB [D_PROTOCOL_STATE]));
	SEND_STATE (DB [D_TIM]);
	end %;

macro
    FLUSH_QUEUE (QUEUE, RETURN_CALL) =
	while 1 eql 1 do
	    begin

	    local
		CCB;

	    if not CMQRM$ (QUEUE, CCB) then exitloop;

	    RETURN_CALL (.CCB %if not %null (%remaining) %then , %remaining %fi);
	    end %;

!
! EQUATED SYMBOLS:
!

literal
    TRUE = 1 EQL 1,
    FALSE = 1 EQL 0,
    NO_OPERATION = 0;

macro
    C_HANDLE = %fieldexpand (C_LIX, 0), 0, 16, 0 %;

global literal
    %name ('DD.INI') = DD_CLEARED,
    %name ('D.LEN') = %fieldexpand (D_BUF, 0)^1;

!
! OWN STORAGE:
!

external routine
    $DSPCR : novalue;

bind
    DMC_TABLE = TABLE$ ($DSPCR, FC_CCP,
	(FC_XME, DMCXME),			!  Transmit enable
	(FC_RCE, DMCRCE),			!  Receive enable
	(FC_CTL, DMCCTL),			!  Control enable
	(FC_TIM, DMCTIM));			!  Timeout


$MCB_PROCESS (
    NAME = DMC,					! Process name
    DLC_DISPATCH = DMC_TABLE)			! DLC dispatch vector
!
! EXTERNAL REFERENCES:
!

external routine
    DMCDBI : novalue,
    DMCDVI,
    DMCNM : MCB_DB_CCB novalue,
    DMCNMI;

external
    MCB$GA_PROCESS_DISPATCH,
    MCB$GAW_PROCESS_DATA_BASE : vector [2],
    MCB$GW_PROCESS_HANDLE,
    MCB$GW_RDB_SIZE;		! Size of RDB buffer.

bind
    DB_BIAS = MCB$GAW_PROCESS_DATA_BASE [0];
routine BASE_IN (DB, LOW, HIGH) : DMC_DB_LOW_HIGH novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	TEMP;

    $MCB_CONVERT_TO_UBA_ADDRESS ((.DB_BIAS, DB [D_LNTB]), .LOW, TEMP);
    .HIGH = (TEMP = .TEMP^%fieldexpand (BA_HIGH, 1));
    end;			!of routine BASE_IN
routine CHECK_BA (DB, BIAS, ADDR) : DMC_DB_BIAS_ADDR =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	?
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    bind
	BUF = DB [D_OUTPUT_DATA] : block field (DMC_FIELDS);

    local
	HIGH,
	LOW;

    if .ADDR eqla 0 then return TRUE;

    $MCB_CONVERT_TO_UBA_ADDRESS ((.BIAS, .ADDR), LOW, HIGH);

    if .LOW nequ .BUF [$SUB_FIELD (SEL4, BA_LOW)] then return FALSE;

    if not .DB [DF_DMR] then return TRUE;

    if .HIGH eqlu .BUF [$SUB_FIELD (SEL6, BA_HIGH)] then return TRUE;

    FALSE
    end;				! of routine CHECK_BA
routine CONTROL_OUT (DB, ERROR) : DMC_DB_ERR novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	ERROR : block [1] field (DMC_FIELDS);

    if .ERROR [O_RUN]			! Buffer not available
    then
	begin

	if FALSE %(.DB [D_ASSIGNED_RECEIVE_COUNT] neq 0)%
	then
	    SEND_TRANSIENT_ERROR (DB [D_TIM], DLL$_TRANSMIT_THRESHOLD);

	return;
	end;

    if .ERROR [DATA_CK]			! Non-fatal error
    then
	begin

	external routine
	    PARAMETER_CIRCUIT_COUNTERS : LINKAGE_DB novalue;

	$NM_DLL_EVENT (4, .DB [D_CIRCUIT_ENTITY], ! Transmit Threshold Error
	    PARAMETER_CIRCUIT_COUNTERS (DB [D_TIM]));

	SEND_TRANSIENT_ERROR (DB [D_TIM], DLL$_RECEIVE_THRESHOLD);
	return;
	end;

    if .ERROR [DISC]			! Non-fatal error
    then
	begin

	local
	    STATE;

	STATE = 0;
	$NM_PLL_EVENT (0, .DB [D_LINE_ENTITY], ! Data Set Ready Transition
	    PARAMETER_C_1 (1, STATE));
	return;
	end;

    if .ERROR [TIME_OUT]		! Non-fatal error
    then
	begin

	external routine
	    PARAMETER_CIRCUIT_COUNTERS : LINKAGE_DB novalue;

	$NM_DLL_EVENT (3, .DB [D_CIRCUIT_ENTITY], ! Transmit Threshold Error
	    PARAMETER_CIRCUIT_COUNTERS (DB [D_TIM]));

	SEND_TRANSIENT_ERROR (DB [D_TIM], DLL$_TRANSMIT_THRESHOLD);
	return;
	end;

    if .ERROR [PROC_ERR] and .DB [DF_FLUSHING_DMC] ! Procedure error
    then
	begin
	DB [DF_FLUSHING_DMC] = FALSE;
	DB [D_DEVICE_STATE] = DD_HALTED;
	return;
	end;

    if .ERROR [STR_RCVD]
    then
	begin

	if .DB [D_PROTOCOL_STATE] eqlu DS_MAINTENANCE
	then
	    $NM_DLL_EVENT (2, .DB [D_CIRCUIT_ENTITY]);

	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_START_RECEIVED);
	DEVICE_SHUT_DOWN (DB [D_TIM]);
	SET_PORT (DB [D_TIM]);
	return;
	end;

    if .ERROR [DATA_LOST]
    then
	begin
	$NM_DLL_EVENT (9, .DB [D_CIRCUIT_ENTITY],
	    PARAMETER_DU_2 (8, MCB$GW_RDB_SIZE));
	DEVICE_SHUT_DOWN (DB [D_TIM]);
	return;
	end;

    if .ERROR [MNT_RCVD]		! Fatal error
    then
	begin
	$NM_DLL_EVENT (96^6 + 13, .DB [D_CIRCUIT_ENTITY]);
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_MAINTENANCE_RECEIVED);
	DEVICE_SHUT_DOWN (DB [D_TIM]);
	return;
	end;

    if .ERROR [NXM]			! Fatal error
    then
	begin
	$NM_PLL_EVENT (3, .DB [D_LINE_ENTITY], ! Memory Access Error
	    PARAMETER_DEVICE_REGISTERS (DB [D_OUTPUT_DATA]));
	COUNTER_INCREMENT (DB, D_NXM_TO_DEVICE);
	COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	DEVICE_SHUT_DOWN (DB [D_TIM]);
	return;
	end;

    $NM_PLL_EVENT (4, .DB [D_LINE_ENTITY],
	PARAMETER_DEVICE_REGISTERS (DB [D_OUTPUT_DATA]));
    SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);

    if .ERROR [PROC_ERR]
    then
	begin
	COUNTER_INCREMENT (DB, D_PROCEDURE_ERROR);
	COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
	DEVICE_SHUT_DOWN (DB [D_TIM]);
	return;
	end;

    COUNTER_INCREMENT (DB, D_INVALID_REGISTER_CONTENTS);
    COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
    MASTER_CLEAR (DB [D_TIM]);
    end;			!of routine CONTROL_OUT
routine CTLNXT (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    DB [DF_CTL] = FALSE;

    if not $MCB_DEQUEUE_CCB (DB [D_PENDING_CONTROLS], CCB) then return;

    CCB [C_FNC] = FC_CCP;
    CCB [C_STS] = CURRENT_STATUS (DB [D_TIM]);
    $MCB_SCHEDULE_CCB (.CCB);
    end;			!of routine CTLNXT
routine CTLSTP

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (DB : ref block field (D_FIELDS),
	CCB : ref block field (C_FIELDS))
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!
	: MCB_DB_CCB novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin
    DB [DF_NOTIFY_OFF] = FALSE;
    DB [DF_NOTIFY_STATE] = FALSE;
    DB [D_TRANSIENT_ERROR] = 0;
    DB [D_PERSISTENT_ERROR] = 0;
    DB [D_OWNER_HANDLE] = 0;

    if .DB [D_ACTIVE_BUFFERS] neq 0
    then
	begin
	! Wait for transmits and receives to complete
	CMQIF$ (DB [D_PENDING_CONTROLS], .CCB);
	DB [DF_CTL] = TRUE;
	return;
	end;

    CCB [C_FNC] = FC_CCP;
    CCB [C_STS] = CURRENT_STATUS (DB [D_TIM]);
    $MCB_SCHEDULE_CCB (.CCB);
    end;			!of routine CTLSTP
routine CTLSTR

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (DB : ref block field (D_FIELDS),
	CCB : ref block field (C_FIELDS))
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!
	: MCB_DB_CCB novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    DB [D_OWNER_HANDLE] = .CCB [C_HANDLE];

    if .CCB [C_PRM1] eql 0
    then
	CCB [C_PRM1] = .DB [D_CIRCUIT_COST];

    CCB [C_STS] = CURRENT_STATUS (DB [D_TIM]);
    CCB [C_FNC] = FC_CCP;
    $MCB_SCHEDULE_CCB (.CCB);
    end;			!of routine CTLSTR
routine CURRENT_STATUS (DB) : LINKAGE_DB =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    literal
	DLL_OFF = 0,
	DLL_SYNCHRONIZING = 1,
	DLL_RUNNING = 2,
	DLL_MAINTENANCE = 3;

    bind
	REPORT_STATUS = TABLE$ (0, 0,
	    (DS_HALTED^1, DLL_OFF),
	    (DS_ISTRT^1, DLL_SYNCHRONIZING),
	    (DS_ASTRT^1, DLL_SYNCHRONIZING),
	    (DS_RUNNING^1, DLL_RUNNING),
	    (DS_MAINTENANCE^1, DLL_MAINTENANCE)) : vector,
	DLL_STATUS = TABLE$ (0, 0,
	    (DLL_OFF^1, DLL$_STATE_OFF),
	    (DLL_SYNCHRONIZING^1, DLL$_STATE_SYNCHRONIZING),
	    (DLL_RUNNING^1, DLL$_STATE_RUNNING),
	    (DLL_MAINTENANCE^1, DLL$_STATE_MAINTENANCE)) : vector;

    if .DB [DF_NOTIFY_OFF]
    then
	begin
	DB [DF_NOTIFY_OFF] = FALSE;

	if .DB [D_PROTOCOL_STATE] eql DS_HALTED
	then
	    DB [DF_NOTIFY_STATE] = FALSE;

	return .DLL_STATUS [DB [D_REPORTED_STATE] = DLL_OFF];
	end;

    DB [DF_NOTIFY_STATE] = FALSE;
    .DLL_STATUS [DB [D_REPORTED_STATE] = .REPORT_STATUS [.DB [D_PROTOCOL_STATE]]]
    end;			!of routine CURRENT_STATUS
routine DEVICE_SHUT_DOWN

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (DB : ref block field (D_FIELDS))
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!
	: LINKAGE_DB novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin
    CHANGE_PROTOCOL_STATE (DB [D_TIM], DS_HALTED, RMT);
    DB [D_DEVICE_STATE] = DD_SHUT_DOWN;
    DB [DF_FLUSHING_DMC] = FALSE;
    DB [D_INPUT_COMMAND] = 0;
    DB [DF_PROTOCOL_UP] = FALSE;
    DB [DC_MASTER_CLEAR] = FALSE;
    DB [DC_BASE_IN] = FALSE;
    DB [DC_CONTROL_IN] = FALSE;
    DB [DC_SET_REQUESTED_STATE] = FALSE;
    end;			!of routine DEVICE_SHUT_DOWN
routine DLLINI (DB, CCB) : DMC_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = address of CCB.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    DB [DC_HALT] = TRUE;
    DB [DC_MASTER_CLEAR] = TRUE;
    DB [D_REQUESTED_STATE] = DS_RUNNING;
    DB [DC_SET_REQUESTED_STATE] = TRUE;
    CCB [C_FNC] = FC_XCP;
    CCB [C_STS] = DLL$_SUCCESS;
    $MCB_SCHEDULE_CCB (.CCB);

    if .DB [D_DEVICE_STATE] eql DD_CLEARED
    then
	begin
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	SEND_STATE (DB [D_TIM]);
	end;

    end;			!of routine DLLINI
routine DLLMOP (DB, CCB) : DMC_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = address of CCB.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    DB [DC_HALT] = TRUE;
    DB [DC_MASTER_CLEAR] = TRUE;
    DB [D_REQUESTED_STATE] = DS_MAINTENANCE;
    DB [DC_SET_REQUESTED_STATE] = TRUE;
    CCB [C_FNC] = FC_XCP;
    CCB [C_STS] = DLL$_SUCCESS;
    $MCB_SCHEDULE_CCB (.CCB);

    if .DB [D_DEVICE_STATE] eql DD_CLEARED
    then
	begin
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	SEND_STATE (DB [D_TIM]);
	end;

    end;			!of routine DLLMOP
routine DLLSTP (DB, CCB) : DMC_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = address of CCB.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    DB [DC_HALT] = TRUE;
    DB [DC_MASTER_CLEAR] = TRUE;
    DB [D_REQUESTED_STATE] = DS_HALTED;
    DB [DC_SET_REQUESTED_STATE] = TRUE;
    CCB [C_FNC] = FC_XCP;
    CCB [C_STS] = DLL$_SUCCESS;
    $MCB_SCHEDULE_CCB (.CCB);
    end;			!of routine DLLSTP
routine DMC_INTERRUPT (DB) : MCB_INTERRUPT novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    if .DB [DF_SYNCHRONIZING]
    then
	return
    else
	DB [DF_SYNCHRONIZING] = TRUE;

    SYNCH$ (.DB [D_SYNCH_BLOCK], DMC_SYNCHRONIZE);
    end;			!of routine DMC_INTERRUPT
routine DMC_SYNCHRONIZE (DB) : MCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    $DMC_CLEAR_BITS (.DB [D_REGISTER], SEL0, IEI);
    DB [DF_SYNCHRONIZING] = FALSE;
    SERVICE_DMC (DB [D_TIM]);
    end;			!of routine DMC_SYNCHRONIZE
routine DMCCTL

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (DB : ref block field (D_FIELDS),
	CCB : ref block field (C_FIELDS),
	MODIFIER)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!
	: MCB_DB_CCB_MOD novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin
    DISPATCH$ (.MODIFIER,
	TABLE$ ($DSPCR, FD_CTL,		! Control dispatch table:
	    (FM_NM, DMCNM),		!  Network Management
	    (FM_STR, CTLSTR),		!  Start line
	    (FM_STP, CTLSTP)),		!  Stop line
	(DB [D_TIM], .CCB),
	MCB_DB_CCB);
    SET_PORT (DB [D_TIM]);
    end;			!of routine DMCCTL
global routine DMCDB

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (BIAS,
	DB : ref block field (D_FIELDS))
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!
	: CALL$ novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin
    MCB$GAW_PROCESS_DATA_BASE [0] = .BIAS;
    MCB$GAW_PROCESS_DATA_BASE [1] = DB [D_TIM];
    MCB$GA_PROCESS_DISPATCH = DMC_TABLE;
    DMCDBI ();
    DMCNMI ();
    DMCDVI ();

    if .DB [D_LINE_STATE] eql DL_ON then $MCB_ENABLE_LONG_TIMER ();

    end;			!of routine DMCDB
routine DMCRCE (DB, CCB) : MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = 
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    RDBRT$ (.CCB);
    DB [D_ACTIVE_BUFFERS] = .DB [D_ACTIVE_BUFFERS] - 1;

    if .DB [D_ACTIVE_BUFFERS] neq 0 then return;

    if .DB [DF_CTL] then CTLNXT (DB [D_TIM]);

    end;			!of routine DMCRCE
routine DMCTIM (DB, MODIFIER) : MCB_DB_MOD novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	MODIFIER = timer function modifier.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    DISPATCH$ (
	.MODIFIER,
	TABLE$ ($DSPCR, FD_TIM,		! Timer dispatch table:
	    (FM_PIN, TIMPIN),		!  Process initialization
	    (FM_PWF, TIMPWF),		!  Power failure
	    (FM_LTM, TIMLTM),		!  Long timer
	    (FM_CCB, TIMCCB),		!  CCB available
	    (FM_RDB, TIMRDB)),		!  RDB available
	(DB [D_TIM]),
	MCB_DB);
    end;			!of routine DMCTIM
routine DMCXME (DB, CCB, MODIFIER) : MCB_DB_CCB_MOD novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = address of transmit CCB.
!	MODIFIER = transmit function modifier (from C_MOD of CCB).
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    DISPATCH$ (
	.MODIFIER,
	TABLE$ ($DSPCR, 0,
	    (DLL$K_ENTER_MAINTENANCE, DLLMOP),
	    (DLL$K_INITIALIZE_LINK, DLLINI),
	    (DLL$K_STOP_LINK, DLLSTP),
	    (DLL$K_TRANSMIT, QUEUE_TRANSMIT),
	    (DLL$K_TRANSMIT_AND_TIME, QUEUE_TRANSMIT)),
	(DB [D_TIM], .CCB),
	DMC_DB_CCB);
    SET_PORT (DB [D_TIM]);
    end;			!of routine DMCXME
routine FLUSH_QUEUES (DB) : DMC_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    RDBCN$ ();
    FLUSH_QUEUE (DB [D_ASSIGNED_TRANSMIT_CCBS], %quote SCHEDULE_TRANSMIT, DB [D_TIM], DLL$_ABORTED);
    FLUSH_QUEUE (DB [D_ASSIGNABLE_TRANSMIT_CCBS], %quote SCHEDULE_TRANSMIT, DB [D_TIM], DLL$_ABORTED);
    FLUSH_QUEUE (DB [D_ASSIGNED_RECEIVE_CCBS], %quote RDBRT$);
    FLUSH_QUEUE (DB [D_ASSIGNABLE_RECEIVE_CCBS], %quote RDBRT$);
    DB [D_ASSIGNABLE_TRANSMIT_COUNT] = 0;
    DB [D_ASSIGNABLE_RECEIVE_COUNT] = 0;
    end;			!of routine FLUSH_QUEUES
global routine MASTER_CLEAR (DB) : LINKAGE_DB =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	STATUS;

    STATUS = FALSE;

    if .DB [D_DEVICE_STATE] eql DD_CLEARED then return .STATUS;

    $DMC_WRITE (.DB [D_REGISTER], BSEL2, 0);
    $DMC_WRITE (.DB [D_REGISTER], SEL0, 1^%fieldexpand (MC, 1));

    do
	begin

	if not $DMC_BITS_CLEAR (.DB [D_REGISTER], SEL0, RUN) then exitloop STATUS = TRUE;

	end
    until (STATUS = .STATUS - 1) eql FALSE;

    if .DB [D_PROTOCOL_STATE] neq DS_HALTED
    then
	CHANGE_PROTOCOL_STATE (DB [D_TIM], DS_HALTED, LCL);

    if .DB [D_DEVICE_STATE] neq DD_MASTER_CLEARED
    then
	begin
	FLUSH_QUEUES (DB [D_TIM]);
	DB [D_ASSIGNED_TRANSMIT_COUNT] = 0;
	DB [D_ASSIGNED_RECEIVE_COUNT] = 0;
	DB [D_DEVICE_STATE] = DD_MASTER_CLEARED;
	DB [D_INPUT_COMMAND] = 0;
	DB [DF_PROTOCOL_UP] = FALSE;
	end;

    .STATUS
    end;			!of routine MASTER_CLEAR
routine OUTPUT_TRANSFER (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    macro
	REG (FLD) = DB [$SUB_FIELD (D_OUTPUT_DATA, FLD)] %;

    field
	CMD = [$SUB_FIELD (SEL2, TYPEO)];

    case .REG [CMD] from 0 to 7 of
	set
	[BA_CC_O + XMT] :
	    begin
	    TRANSMIT_OUT (DB [D_TIM], .REG [SEL4], .REG [SEL6]);
	    DB [D_ASSIGNED_TRANSMIT_COUNT] = .DB [D_ASSIGNED_TRANSMIT_COUNT] - 1;
	    RESET_TIMER (DB [D_TIM]);
	    end;
	[BA_CC_O + RCV] :
	    begin
	    RECEIVE_OUT (DB [D_TIM], .REG [SEL4], .REG [SEL6]);
	    DB [D_ASSIGNED_RECEIVE_COUNT] = .DB [D_ASSIGNED_RECEIVE_COUNT] - 1;
	    end;
	[CNTL_O + XMT, CNTL_O + RCV] :
	    begin
	    CONTROL_OUT (DB [D_TIM], .REG [SEL6]);
	    end;
	[inrange] :
	    begin
	    $NM_PLL_EVENT (4, .DB [D_LINE_ENTITY],
		PARAMETER_DEVICE_REGISTERS (REG [SEL0]));
	    COUNTER_INCREMENT (DB, D_INVALID_REGISTER_CONTENTS);
	    COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
	    SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	    MASTER_CLEAR (DB [D_TIM]);
	    end;
	tes;

    SET_PORT (DB [D_TIM]);
    end;			!of routine OUTPUT_TRANSFER
global routine PARAMETER_DEVICE_REGISTERS

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!
       (REG : ref vector)
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
	: DMC_REG novalue =
!
! SIDE EFFECTS:
!	None
!--

    begin

    !
    ! DMC registers
    !

    PARAMETER_H_2 (0, REG [0]);
    REG = REG [1];
    PARAMETER_H_2 (0, REG [0]);
    REG = REG [1];
    PARAMETER_H_2 (0, REG [0]);
    REG = REG [1];
    PARAMETER_H_2 (0, REG [0]);
    end;			!of routine PARAMETER_DEVICE_REGISTERS
routine QUEUE_TRANSMIT (DB, CCB) : DMC_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = address of CCB.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    local
	MSG_CNT;

    if .DB [D_PROTOCOL_STATE] eql DS_MAINTENANCE
    then
	begin
	local
	    SAVE_MAP;
	SMAP$ (SAVE_MAP);
	MAP$ (.CCB [C_BIAS]);
	selectone ch$rchar (.CCB [C_ADDR]) of
	    set
	    [8, 12]:
		ch$wchar (12, ch$plus (.CCB [C_ADDR], 1));
	    tes;
	MAP$ (.SAVE_MAP);
	end;

    DB [D_ACTIVE_BUFFERS] = .DB [D_ACTIVE_BUFFERS] + 1;
    DB [D_ASSIGNABLE_TRANSMIT_COUNT] = .DB [D_ASSIGNABLE_TRANSMIT_COUNT] + 1;
    CMQIN$ (DB [D_ASSIGNABLE_TRANSMIT_CCBS], .CCB);

    if .CCB [C_CHN] eqla 0 then return;

    MSG_CNT = .DB [D_TRANSMIT_BUFFER_SIZE];

    do
	begin

	if (MSG_CNT = .MSG_CNT - .CCB [C_CNT]) lss 0
	then
	    SIGNAL_STOP (DMC$_MTL, .DB [$SUB_FIELD (D_ASSIGNABLE_TRANSMIT_CCBS, LIST_LAST)]);

	end
    while (CCB = .CCB [C_CHN]) neq 0;

    end;			!of routine QUEUE_TRANSMIT
routine RECEIVE_IN (DB, LOW, HIGH) : DMC_DB_LOW_HIGH novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    if not CMQRM$ (DB [D_ASSIGNABLE_RECEIVE_CCBS], CCB)
    then
	return SIGNAL_STOP (DMC$_QUE, DB [D_ASSIGNABLE_RECEIVE_CCBS]);

    DB [D_ASSIGNABLE_RECEIVE_COUNT] = .DB [D_ASSIGNABLE_RECEIVE_COUNT] - 1;
    CMQIN$ (DB [D_ASSIGNED_RECEIVE_CCBS], .CCB);
    begin

    local
	TEMP;

    $MCB_CONVERT_TO_UBA_ADDRESS ((.CCB [C_BIAS], .CCB [C_ADDR]), .LOW, TEMP);
    TEMP = .TEMP^%fieldexpand (BA_HIGH, 1);
    .HIGH = (TEMP = .TEMP + .MCB$GW_RDB_SIZE);
    end;
    end;			!of routine RECEIVE_IN
routine RECEIVE_OUT (DB, LOW, HIGH) : DMC_DB_LOW_HIGH novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	HIGH : block [1] field (DMC_FIELDS),
	LOW : block [1] field (DMC_FIELDS);

    local
	CCB : ref block field (C_FIELDS);

    if not CMQRM$ (DB [D_ASSIGNED_RECEIVE_CCBS], CCB)
    then
	SIGNAL_STOP (DMC$_QUE, DB [D_ASSIGNED_RECEIVE_CCBS]);

    if not CHECK_BA (DB [D_TIM], .CCB [C_BIAS], .CCB [C_ADDR])
    then
	begin
	RDBRT$ (.CCB);
	COUNTER_INCREMENT (DB, D_RECEIVE_BA_MISMATCH);
	COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	MASTER_CLEAR (DB [D_TIM]);
	return;
	end;

    CCB [C_CNT] = .HIGH [CC];

    if .DB [D_PROTOCOL_STATE] eql DS_RUNNING
    then
	begin
	COUNTER_ADD (DB, D_BYTES_RECEIVED, .CCB [C_CNT]);
	COUNTER_INCREMENT (DB, D_BLOCKS_RECEIVED);
	end;

    DB [DF_PROTOCOL_UP] = TRUE;
    SCHEDULE_RECEIVE (.CCB, DB [D_TIM]);
    DB [DF_WAITING_FOR_RECEIVE] = FALSE;
    end;			!of routine RECEIVE_OUT
routine RESET_TIMER (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    DB [D_TIM] = 0;

    selectone TRUE of
	set
	[.DB [DF_WAITING_FOR_RDYI]] :
	    DB [D_TIM] = .DB [D_TIM] + 1;

	[.DB [DF_FLUSHING_DMC]] :
	    DB [D_TIM] = 3;

	[.DB [D_TRANSIENT_ERROR] neq 0] :
	    DB [D_TIM] = 2;

	[.DB [D_PERSISTENT_ERROR] neq 0] :
	    DB [D_TIM] = 2;

	[.DB [D_ASSIGNED_TRANSMIT_COUNT] neq 0] :
	    DB [D_TIM] = .DB [D_TRANSMIT_TIMEOUT];

	[.DB [DF_WAITING_FOR_RECEIVE]] :
	    DB [D_TIM] = .DB [D_SERVICE_TIMER];

	tes;
    end;			!of routine RESET_TIMER
routine SCHEDULE_PERSISTENT_AST (CCB, DB) : DMC_CCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	CCB = address of CCB to use for status nodification.
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    CCB [C_HANDLE] = .DB [D_OWNER_HANDLE];
    CCB [C_FNC] = FC_AST;
    CCB [C_MOD] = DLL$K_PERSISTENT_ERROR;
    CCB [C_STS] = .DB [D_PERSISTENT_ERROR];
    $MCB_SCHEDULE_CCB (.CCB);
    DB [D_PERSISTENT_ERROR] = 0;
    end;			!of routine SCHEDULE_PERSISTENT_AST
routine SCHEDULE_RECEIVE (CCB, DB) : DMC_CCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    CCB [C_HANDLE] = .DB [D_OWNER_HANDLE];

    if .CCB [C_HANDLE] eqlu 0 then return RDBRT$ (.CCB);

    DB [D_ACTIVE_BUFFERS] = .DB [D_ACTIVE_BUFFERS] + 1;
    CCB [C_FNC] = FC_RCP;
    CCB [C_MOD] = DLL$K_DATA_RECEIVED;
    CCB [C_STS] = DLL$_SUCCESS;
    $MCB_SCHEDULE_CCB (.CCB);
    end;			!of routine SCHEDULE_RECEIVE
routine SCHEDULE_STATE_AST (CCB, DB) : DMC_CCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	CCB = address of CCB to use for status nodification.
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    if not .DB [DF_NOTIFY_STATE] then return $MCB_RETURN_CCB (.CCB);

    CCB [C_HANDLE] = .DB [D_OWNER_HANDLE];
    CCB [C_FNC] = FC_AST;
    CCB [C_MOD] = DLL$K_STATE;
    CCB [C_STS] = CURRENT_STATUS (DB [D_TIM]);
    $MCB_SCHEDULE_CCB (.CCB);

    if .DB [DF_NOTIFY_STATE]
    then

	if $MCB_GET_CCB (CCB)
	then
	    SCHEDULE_STATE_AST (.CCB, DB [D_TIM])
	else
	    $MCB_REQUEST_CCB ();

    end;			!of routine SCHEDULE_STATE_AST
routine SCHEDULE_TRANSIENT_AST (CCB, DB) : DMC_CCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	CCB = address of CCB to use for status nodification.
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
      GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    CCB [C_HANDLE] = .DB [D_OWNER_HANDLE];
    CCB [C_FNC] = FC_AST;
    CCB [C_MOD] = DLL$K_TRANSIENT_ERROR;
    CCB [C_STS] = .DB [D_TRANSIENT_ERROR];
    $MCB_SCHEDULE_CCB (.CCB);
    DB [D_TRANSIENT_ERROR] = 0;
    end;			!of routine SCHEDULE_TRANSIENT_AST
routine SCHEDULE_TRANSMIT (CCB, DB, STATUS) : DMC_CCB_DB_STS novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    CCB [C_FNC] = FC_XCP;
    CCB [C_STS] = .STATUS;
    $MCB_SCHEDULE_CCB (.CCB);

    DB [D_ACTIVE_BUFFERS] = .DB [D_ACTIVE_BUFFERS] - 1;

    if .DB [D_ACTIVE_BUFFERS] neq 0 then return;

    if .DB [DF_CTL] then CTLNXT (DB [D_TIM]);

    end;			!of routine SCHEDULE_TRANSMIT
routine SEND_PERSISTENT_ERROR (DB, STATUS) : DMC_DB_STS novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    if .DB [D_OWNER_HANDLE] eql 0 then return;

    DB [D_PERSISTENT_ERROR] = .STATUS;

    if $MCB_GET_CCB (CCB)
    then
	SCHEDULE_PERSISTENT_AST (.CCB, DB [D_TIM])
    else
	$MCB_REQUEST_CCB ();

    end;			!of routine SEND_PERSISTENT_ERROR
routine SEND_STATE (DB) : DMC_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    if .DB [D_OWNER_HANDLE] eql 0 then return;

    if .DB [D_PROTOCOL_STATE] eql DS_HALTED then DB [DF_NOTIFY_OFF] = TRUE;

    DB [DF_NOTIFY_STATE] = TRUE;

    if $MCB_GET_CCB (CCB)
    then
	SCHEDULE_STATE_AST (.CCB, DB [D_TIM])
    else
	$MCB_REQUEST_CCB ();

    end;			!of routine SEND_STATE
routine SEND_TRANSIENT_ERROR (DB, STATUS) : DMC_DB_STS novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	CCB : ref block field (C_FIELDS);

    if .DB [D_OWNER_HANDLE] eql 0 then return;

    DB [D_TRANSIENT_ERROR] = .STATUS;

    if $MCB_GET_CCB (CCB)
    then
	SCHEDULE_TRANSIENT_AST (.CCB, DB [D_TIM])
    else
	$MCB_REQUEST_CCB ();

    end;			!of routine SEND_TRANSIENT_ERROR
routine SERVICE_DMC (DB) : MCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    label
	SERVICE_FOUND;

    bind
	CSR = .DB [D_REGISTER];

    DB [DF_SERVICING_DMC] = TRUE;

    do
	SERVICE_FOUND : begin

	case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
	    set
	    [DD_CLEARED, DD_MASTER_CLEARED, DD_SHUT_DOWN] :
		begin
		DB [DF_SERVICING_DMC] = FALSE;
		return;
		end;
	    [inrange] :
		NO_OPERATION;
	    tes;

	decru COUNT from 4 to 1 do
	    begin

	    if not $DMC_BITS_CLEAR (CSR, SEL0, RDYI)
	    then
		begin

		if not $DMC_BITS_CLEAR (CSR, SEL0, RQI)
		then
		    begin
		    !
		    ! We have requested the input port and
		    ! the DMC has complied.
		    !

		    bind
			BUF = DB [D_INPUT_DATA] : block field (DMC_FIELDS);

		    DB [DF_WAITING_FOR_RDYI] = FALSE;
		    RESET_TIMER (DB [D_TIM]);
		    $DMC_WRITE (CSR, SEL6, .BUF [SEL6]);
		    $DMC_WRITE (CSR, SEL4, .BUF [SEL4]);
		    $DMC_CLEAR_BITS (CSR, SEL0, RQI);
		    DB [D_INPUT_COMMAND] = 0;
		    SET_INPUT_REQUEST (DB [D_TIM]);
		    leave SERVICE_FOUND;
		    end
		else
		    begin
		    !
		    ! We have given the DMC a command
		    ! the DMC is processing it.
		    !
		    NO_OPERATION
		    end;

		end
	    else
		begin

		if $DMC_BITS_CLEAR (CSR, SEL0, RQI)
		then
		    begin
		    !
		    ! No input transfer operation in progress.
		    !

		    if .DB [D_INPUT_COMMAND] neq 0
		    then
			begin

			bind
			    BUF = DB [D_INPUT_DATA] : block field (DMC_FIELDS);

			$DMC_WRITE (CSR, BSEL0, .BUF [BSEL0]);
			DB [DF_WAITING_FOR_RDYI] = TRUE;
			RESET_TIMER (DB [D_TIM]);
			leave SERVICE_FOUND;
			end;

		    end
		else
		    begin
		    !
		    ! We have requested the input port and
		    ! the DMC hasn't complied yet.
		    !
		    NO_OPERATION
		    end;

		end;

	    if not $DMC_BITS_CLEAR (CSR, SEL2, RDYO)
	    then
	    begin
		!
		! The DMC has data for us.
		!

		bind
		    BUF = DB [D_OUTPUT_DATA] : block field (DMC_FIELDS);

		BUF [SEL0] = $DMC_READ (CSR, SEL0);
		BUF [SEL2] = $DMC_READ (CSR, SEL2);
		BUF [SEL4] = $DMC_READ (CSR, SEL4);
		BUF [SEL6] = $DMC_READ (CSR, SEL6);
		$DMC_CLEAR_BITS (CSR, SEL2, RDYO);
		OUTPUT_TRANSFER (DB [D_TIM]);
		leave SERVICE_FOUND;
		end;

	    end;

	if not $DMC_BITS_CLEAR (CSR, SEL0, RQI)
	then
	    begin
	    $DMC_SET_BITS (CSR, SEL0, IEI);
	    $DMC_SET_BITS (CSR, SEL0, IEI);
	    end;

	DB [DF_SERVICING_DMC] = FALSE;
	return;
	end
    while TRUE;

    end;			!of routine SERVICE_DMC
routine SET_INPUT_REQUEST (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    bind
	BUF = DB [D_INPUT_DATA] : block field (DMC_FIELDS);

    while .DB [D_CONTROL_FLAGS] neq 0 do

        selectone TRUE of
	    set
	    [.DB [DC_HALT]] :
		begin

		if .DB [DF_FLUSHING_DMC] then return;

		case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
		    set
		    [DD_RUNNING] :
			begin
			DB [DF_FLUSHING_DMC] = TRUE;
			RESET_TIMER (DB [D_TIM]);
			BUF [SEL6] = 0;
			BUF [SEL4] = 0;
			BUF [BSEL0] = HALT + 1^%fieldexpand (RQI, 1);
			return;
			end;
		    [DD_CLEARED, DD_MASTER_CLEARED, DD_HALTED] :
			DB [DC_HALT] = FALSE;
		    [inrange] :
			begin
			DB [DC_MASTER_CLEAR] = TRUE;
			DB [DC_HALT] = FALSE;
			end;
		    tes;

		end;
	    [.DB [DC_MASTER_CLEAR]] :
		case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
		    set
		    [DD_CLEARED, DD_MASTER_CLEARED] :
			DB [DC_MASTER_CLEAR] = FALSE;
		    [inrange] :
			begin
			MASTER_CLEAR (DB [D_TIM]);
			DB [DC_MASTER_CLEAR] = FALSE;
			end;
		    tes;
	    [.DB [DC_ZERO_CIRCUIT_COUNTERS]] :
		begin
		ZERO_CIRCUIT_COUNTERS (DB [D_TIM]);
		DB [DC_ZERO_CIRCUIT_COUNTERS] = FALSE;
		end;
	    [.DB [DC_ZERO_LINE_COUNTERS]] :
		begin
		ZERO_LINE_COUNTERS (DB [D_TIM]);
		DB [DC_ZERO_LINE_COUNTERS] = FALSE;
		end;
	    [.DB [DC_BASE_IN]] :
		case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
		    set
		    [DD_RUNNING] : DB [DC_HALT] = TRUE;
		    [DD_HALTED, DD_MASTER_CLEARED] :
			begin

			if .DB [D_DEVICE_STATE] eql DD_MASTER_CLEARED
			then
			    $DMC_SET_BITS (.DB [D_REGISTER], SEL2, IEO);

			BASE_IN (DB [D_TIM], BUF [SEL4], BUF [SEL6]);
			BUF [$SUB_FIELD (SEL6, RESUME)] = 0;

			if .DB [D_DEVICE_STATE] neq DD_MASTER_CLEARED
			then
			    BUF [$SUB_FIELD (SEL6, RESUME)] = 1;

			BUF [BSEL0] = BASE_I + 1^%fieldexpand (RQI, 1);
			DB [DC_BASE_IN] = FALSE;
			DB [D_DEVICE_STATE] = DD_BASE_TABLE_ASSIGNED;
			return;
			end;
		    [DD_BASE_TABLE_ASSIGNED, DD_CLEARED] : DB [DC_BASE_IN] = FALSE;
		    [inrange] : DB [DC_MASTER_CLEAR] = TRUE;
		    tes;
	    [.DB [DC_CONTROL_IN]] :
		case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
		    set
		    [DD_BASE_TABLE_ASSIGNED] :
			begin
			DB [DC_CONTROL_IN] = FALSE;
			BUF [SEL4] = 0;
			BUF [SEL6] = 0;

			if .DB [DF_CONTROLLER_LOOPBACK]
			then
			    $DMC_SET_BITS (.DB [D_REGISTER], SEL0, LU_LOOP)
			else
			    $DMC_CLEAR_BITS (.DB [D_REGISTER], SEL0, LU_LOOP);

			if .DB [DF_HALF_DUPLEX]
			then
			    BUF [$SUB_FIELD (SEL6, HD)] = 1;

			if .DB [D_REQUESTED_STATE] eql DS_MAINTENANCE
			then
			    BUF [$SUB_FIELD (SEL6, MAINT)] = 1;

			if not .DB [DF_1_SEC]
			then
			    BUF [$SUB_FIELD (SEL6, SEC)] = 1;

			BUF [BSEL0] = CNTL_I + 1^%fieldexpand (RQI, 1);
			DB [D_DEVICE_STATE] = DD_RUNNING;
			CHANGE_PROTOCOL_STATE (DB [D_TIM], .DB [D_REQUESTED_STATE], LCL);
			return;
			end;
		    [DD_CLEARED, DD_RUNNING] : DB [DC_CONTROL_IN] = FALSE;
		    [inrange] : DB [DC_BASE_IN] = TRUE;
		    tes;
	    [.DB [DC_SET_REQUESTED_STATE]] :

		selectone TRUE of
		    set
		    [.DB [D_LINE_STATE] neq DL_ON] :
			DB [DC_SET_REQUESTED_STATE] = FALSE;
		    [.DB [D_DEVICE_STATE] eql DD_CLEARED] :
			DB [DC_SET_REQUESTED_STATE] = FALSE;
		    [.DB [D_PROTOCOL_STATE] eql .DB [D_REQUESTED_STATE]] :
			DB [DC_SET_REQUESTED_STATE] = FALSE;
		    [.DB [D_REQUESTED_STATE] eql DS_HALTED] :
			DB [DC_MASTER_CLEAR] = TRUE;
		    [otherwise] :
			DB [DC_CONTROL_IN] = TRUE;
		    tes;

	    [.DB [DC_CONTINUE_CONTROL]] :
		begin
		DB [DC_CONTINUE_CONTROL] = FALSE;

		if .DB [DF_CTL] then CTLNXT (DB [D_TIM]);

		end;
	    tes;

    if .DB [D_DEVICE_STATE] neq DD_RUNNING then return;

    if .DB [D_ASSIGNED_RECEIVE_COUNT] lssu .DB [D_MAXIMUM_RECEIVE_COUNT]
    then

	if .DB [D_ASSIGNABLE_RECEIVE_COUNT] neq 0
	then
	    begin
	    RECEIVE_IN (DB [D_TIM], BUF [SEL4], BUF [SEL6]);
	    BUF [BSEL0] = BA_CC_I + RCV + 1^%fieldexpand (RQI, 1);
	    DB [D_ASSIGNED_RECEIVE_COUNT] = .DB [D_ASSIGNED_RECEIVE_COUNT] + 1;
	    return;
	    end
	else
	    begin

	    local
		RDB : ref block field (C_FIELDS);

	    if RDBGT$ (RDB)
	    then
		begin
		DB [D_ASSIGNABLE_RECEIVE_COUNT] = .DB [D_ASSIGNABLE_RECEIVE_COUNT] + 1;
		CMQIN$ (DB [D_ASSIGNABLE_RECEIVE_CCBS], .RDB);
		RECEIVE_IN (DB [D_TIM], BUF [SEL4], BUF [SEL6]);
		BUF [BSEL0] = BA_CC_I + RCV + 1^%fieldexpand (RQI, 1);
		DB [D_ASSIGNED_RECEIVE_COUNT] = .DB [D_ASSIGNED_RECEIVE_COUNT] + 1;
		return;
		end
	    else
		RDBRQ$ ();

	    end;

    if .DB [D_ASSIGNED_TRANSMIT_COUNT] lssu .DB [D_MAXIMUM_TRANSMIT_COUNT]
    then

	if .DB [D_ASSIGNABLE_TRANSMIT_COUNT] neq 0
	then
	    begin
	    TRANSMIT_IN (DB [D_TIM], BUF [SEL4], BUF [SEL6]);
	    BUF [BSEL0] = BA_CC_I + XMT + 1^%fieldexpand (RQI, 1);
	    DB [D_ASSIGNED_TRANSMIT_COUNT] = .DB [D_ASSIGNED_TRANSMIT_COUNT] + 1;
	    return;
	    end;

    end;			!of routine SET_INPUT_REQUEST
global routine SET_PORT (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    if .DB [D_INPUT_COMMAND] eql 0
    then
	SET_INPUT_REQUEST (DB [D_TIM]);

    if not .DB [DF_SERVICING_DMC] then SERVICE_DMC (DB [D_TIM]);

    end;			!of routine SET_PORT
routine TIMCCB (DB, CCB) : MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = 
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    label
	RECOVERY;

    if .DB [D_OWNER_HANDLE] eql 0 then return $MCB_RETURN_CCB (.CCB);

    do RECOVERY :
	begin

	if .DB [DF_NOTIFY_OFF]
	then
	    leave RECOVERY with SCHEDULE_STATE_AST (.CCB, DB [D_TIM]);

	if .DB [DF_NOTIFY_STATE]
	then
	    leave RECOVERY with SCHEDULE_STATE_AST (.CCB, DB [D_TIM]);

	if .DB [D_TRANSIENT_ERROR] neq 0
	then
	    leave RECOVERY with SCHEDULE_TRANSIENT_AST (.CCB, DB [D_TIM]);

	if .DB [D_PERSISTENT_ERROR] neq 0
	then
	    leave RECOVERY with SCHEDULE_PERSISTENT_AST (.CCB, DB [D_TIM]);

	return $MCB_RETURN_CCB (.CCB);
	end
    while $MCB_GET_CCB (CCB);

    end;			!of routine TIMCCB
routine TIMLTM (DB) : MCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    if not .DB [DF_SYNCHRONIZING] and
	not ($DMC_BITS_CLEAR (.DB [D_REGISTER], SEL0, RDYI) and
	$DMC_BITS_CLEAR (.DB [D_REGISTER], SEL2, RDYO))
    then
	begin
	RESET_TIMER (DB [D_TIM]);
	COUNTER_INCREMENT (DB, D_LOST_INTERRUPT);
	COUNTER_INCREMENT (DB, D_DEVICE_SERVICE_ERRORS);
	SERVICE_DMC (DB [D_TIM]);
	return;
	end;

    if .DB [DF_WAITING_FOR_RDYI]
    then
	begin
	RESET_TIMER (DB [D_TIM]);
	COUNTER_INCREMENT (DB, D_LOST_RDYI);
	COUNTER_INCREMENT (DB, D_DEVICE_SERVICE_ERRORS);
	SET_PORT (DB [D_TIM]);
	! SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_RESPONSE_ERROR);
	return;
	end;

    if .DB [DF_FLUSHING_DMC]
    then
	begin
	DB [DF_FLUSHING_DMC] = FALSE;
	COUNTER_INCREMENT (DB, D_LOST_HALT);
	COUNTER_INCREMENT (DB, D_DEVICE_SERVICE_ERRORS);
	MASTER_CLEAR (DB [D_TIM]);
	RESET_TIMER (DB [D_TIM]);
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_RESPONSE_ERROR);
	CHANGE_PROTOCOL_STATE (DB [D_TIM], DS_HALTED, RMT);
	SET_PORT (DB [D_TIM]);
	return;
	end;

    if .DB [D_ASSIGNED_TRANSMIT_COUNT] neq 0
    then

	if (.DB [D_PROTOCOL_STATE] eql DS_MAINTENANCE) or .DB [DF_PROTOCOL_UP]
	then
	    begin
	    SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_RESPONSE_ERROR);
	    COUNTER_INCREMENT (DB, D_LOST_TRANSMIT);
	    COUNTER_INCREMENT (DB, D_DEVICE_SERVICE_ERRORS);
	    MASTER_CLEAR (DB [D_TIM]);
	    RESET_TIMER (DB [D_TIM]);
	    SET_PORT (DB [D_TIM]);
	    return;
	    end;

    if .DB [DF_WAITING_FOR_RECEIVE]
    then
	begin
	DB [DF_WAITING_FOR_RECEIVE] = FALSE;
	SEND_TRANSIENT_ERROR (DB [D_TIM], DLL$_SERVICE_TIMEOUT);
	RESET_TIMER (DB [D_TIM]);
	return;
	end;

    end;			!of routine TIMLTM
routine TIMPIN (DB) : MCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    if .DB [DF_DMR_LINE] then return;

    DMCDBI ();
    DMCNMI ();
    DMCDVI ();

    if .DB [D_LINE_STATE] eql DL_ON then $MCB_ENABLE_LONG_TIMER ();

    end;			!of routine TIMPIN
routine TIMPWF (DB) : MCB_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    SIGNAL_STOP (DMC$_PWF);
    end;			!of routine TIMPWF
routine TIMRDB (DB, CCB) : MCB_DB_CCB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!	CCB = 
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	CCB : ref block field (C_FIELDS);

    case .DB [D_DEVICE_STATE] from DD_LOW to DD_HIGH of
	set
	[DD_CLEARED, DD_MASTER_CLEARED] :
	    RDBRT$ (.CCB);
	[inrange] :
	    begin
	    DB [D_ASSIGNABLE_RECEIVE_COUNT] = .DB [D_ASSIGNABLE_RECEIVE_COUNT] + 1;
	    CMQIN$ (DB [D_ASSIGNABLE_RECEIVE_CCBS], .CCB);
	    SET_PORT (DB [D_TIM]);
	    end;
	tes;
    end;			!of routine TIMRDB
routine TRANSMIT_IN (DB, LOW, HIGH) : DMC_DB_LOW_HIGH novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    local
	ADDRESS,
	BIAS,
	CCB : ref block field (C_FIELDS),
	LENGTH;

    if not CMQRM$ (DB [D_ASSIGNABLE_TRANSMIT_CCBS], CCB)
    then
	return SIGNAL_STOP (DMC$_QUE, DB [D_ASSIGNABLE_TRANSMIT_CCBS]);

    DB [D_ASSIGNABLE_TRANSMIT_COUNT] = .DB [D_ASSIGNABLE_TRANSMIT_COUNT] - 1;
    CMQIN$ (DB [D_ASSIGNED_TRANSMIT_CCBS], .CCB);

    if .CCB [C_CHN] eql 0
    then
	begin
	BIAS = .CCB [C_BIAS];
	ADDRESS = .CCB [C_ADDR];
	LENGTH = .CCB [C_CNT];
	end
    else
	begin
	BIAS = .DB_BIAS;
	ADDRESS = LENGTH = .DB [D_NEXT_TRANSMIT_BUFFER];
	LENGTH = $MCB_COPY_CHAIN_TO_BUFFER (.CCB, (.BIAS, .ADDRESS));
	MAP$ (.DB_BIAS);
	DB [D_NEXT_TRANSMIT_BUFFER] = .DB [D_NEXT_TRANSMIT_BUFFER] + .DB [D_TRANSMIT_BUFFER_SIZE];

	if .DB [D_NEXT_TRANSMIT_BUFFER] geqa .DB [D_HIGHEST_TRANSMIT_BUFFER]
	then
	    DB [D_NEXT_TRANSMIT_BUFFER] = DB [D_BUF];

	end;

    begin

    local
	TEMP;

    $MCB_CONVERT_TO_UBA_ADDRESS ((.BIAS, .ADDRESS), .LOW, TEMP);
    TEMP = .TEMP^%fieldexpand (BA_HIGH, 1);
    .HIGH = (TEMP = .TEMP + .LENGTH);
    end;
    end;			!of routine TRANSMIT_IN
routine TRANSMIT_OUT (DB, LOW, HIGH) : DMC_DB_LOW_HIGH novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    map
	HIGH : block [1] field (DMC_FIELDS),
	LOW : block [1] field (DMC_FIELDS);
	
    local
	CCB : ref block field (C_FIELDS);

    if not CMQRM$ (DB [D_ASSIGNED_TRANSMIT_CCBS], CCB)
    then
	SIGNAL_STOP (DMC$_QUE, DB [D_ASSIGNED_TRANSMIT_CCBS]);

    if
	not begin

	if .CCB [C_CHN] eqla 0
	then
	    CHECK_BA (DB [D_TIM], .CCB [C_BIAS], .CCB [C_ADDR])
	else
	    CHECK_BA (DB [D_TIM], .DB_BIAS, 0)

	end
    then
	begin
	SCHEDULE_TRANSMIT (.CCB, DB [D_TIM], DLL$_ABORTED);
	COUNTER_INCREMENT (DB, D_TRANSMIT_BA_MISMATCH);
	COUNTER_INCREMENT (DB, D_FATAL_DEVICE_ERRORS);
	SEND_PERSISTENT_ERROR (DB [D_TIM], DLL$_DEVICE_OPERATION_ERROR);
	MASTER_CLEAR (DB [D_TIM]);
	return;
	end;

    selectone .DB [D_PROTOCOL_STATE] of
	set
	[DS_RUNNING] :
	    begin
	    COUNTER_ADD (DB, D_BYTES_TRANSMITTED, .HIGH [CC]);
	    COUNTER_INCREMENT (DB, D_BLOCKS_TRANSMITTED);
	    end;
	[DS_MAINTENANCE] :

	    if .CCB [C_MOD] eql DLL$K_TRANSMIT_AND_TIME
	    then
		DB [DF_WAITING_FOR_RECEIVE] = TRUE;

	tes;

    DB [DF_PROTOCOL_UP] = TRUE;
    SCHEDULE_TRANSMIT (.CCB, DB [D_TIM], DLL$_SUCCESS);
    end;			!of routine TRANSMIT_OUT
routine ZERO_CIRCUIT_COUNTERS (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	The appropriate NM status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)
    COUNTER_ZERO (DB, D_BYTES_RECEIVED);	! [1000]
    COUNTER_ZERO (DB, D_BYTES_TRANSMITTED);	! [1001]
    COUNTER_ZERO (DB, D_BLOCKS_RECEIVED);	! [1010]
    COUNTER_ZERO (DB, D_BLOCKS_TRANSMITTED);	! [1011]

    begin

    macro
	BASE (FLD) = DB [$SUB_FIELD (D_LNTB, FLD)] %,
	COPY (FLD) = DB [$SUB_FIELD (D_CPTB, FLD)] %;

    begin

    local
	CNT,
	FRM_PTR,
	TO_PTR;

    FRM_PTR = BASE [BASE_COUNTER_BEGIN];
    TO_PTR = COPY [BASE_COUNTER_BEGIN];
    CNT = %fieldexpand (BASE_NAKS_RCV_REP, 0)^1 + %fieldexpand (BASE_NAKS_RCV_REP, 1)/8
	- %fieldexpand (BASE_COUNTER_BEGIN, 0)^1 + %fieldexpand (BASE_COUNTER_BEGIN, 1)/8;

    do ch$wchar_a (ch$rchar_a (FRM_PTR), TO_PTR) while (CNT = .CNT - 1) nequ 0;

    end;

    if .DB [DF_DMR]
    then
	begin
	COPY [BASE_NAKS_RCV_REP] = .BASE [BASE_NAKS_RCV_REP];
	COPY [BASE_NAKS_RCV_TOO_LONG] = .BASE [BASE_NAKS_RCV_TOO_LONG];
	COPY [BASE_NAKS_SND_RCV_REP] = .BASE [BASE_NAKS_SND_RCV_REP];
	COPY [BASE_INCOMPLETE_SEL] = .BASE [BASE_INCOMPLETE_SEL];
	COPY [BASE_NO_REPLY_TO_SEL] = .BASE [BASE_NO_REPLY_TO_SEL];
	end;

    end;
    end;
routine ZERO_LINE_COUNTERS (DB) : LINKAGE_DB novalue =

!++
! FUNCTIONAL DESCRIPTION:
!
!
! FORMAL PARAMETERS:
!	DB = address of DMC data base.
!
! IMPLICIT INPUTS:
!	None
!
! IMPLICIT OUTPUTS:
!	None
!
! ROUTINE VALUE:
!	The appropriate NM status code
!
! COMPLETION CODES:
!	None
!
! SIDE EFFECTS:
!	None
!--

    begin
    GET_DMC_DATA_BASE (DB)

    if .DB [DF_DMR]
    then
	begin

	macro
	    BASE (FLD) = DB [$SUB_FIELD (D_LNTB, FLD)] %,
	    COPY (FLD) = DB [$SUB_FIELD (D_CPTB, FLD)] %;

	COPY [BASE_NAKS_RCV_RCV_OVRN] = .BASE [BASE_NAKS_RCV_RCV_OVRN];
	COPY [BASE_NAKS_RCV_HDR_FMT] = .BASE [BASE_NAKS_RCV_HDR_FMT];
	COPY [BASE_NAKS_SND_RCV_OVRN] = .BASE [BASE_NAKS_SND_RCV_OVRN];
	COPY [BASE_NAKS_SND_HDR_FMT] = .BASE [BASE_NAKS_SND_HDR_FMT];
	COPY [BASE_XMT_UNDR] = .BASE [BASE_XMT_UNDR];
	COPY [BASE_STRM_TIMEOUT] = .BASE [BASE_STRM_TIMEOUT];
	end;

    end;
end
eludom