#module SSUBS X


**

**  COPYRIGHT (c) 1993, 1994, 1995, 1997 
**  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETT
**  ALL RIGHTS RESERVE

**  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPI
**  ONLY  IN  ACCORDANCE  OF  THE  TERMS  OF  SUCH  LICENSE  AND WITH T
**  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTH
**  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO A
**  OTHER PERSON.  NO TITLE TO AND  OWNERSHIP OF THE  SOFTWARE IS  HERE
**  TRANSFERRE

**  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTI
**  AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPME
**  CORPORATIO

**  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF I
**  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITA
**


                               

**
**  FACILIT

**      DECamds - DEC Availability Manager for Distrubuted Syste

**  ABSTRAC

**	This module contains a number of utility routines used by the consol
**	the comm process, and RMCP. 

**	NOTE WELL: Don't use any "global" structures... Make everything pass
**		   to the routine; otherwise, the headache of "special
**		   compilation will come into play.

**  MODIFICATION HISTOR
**
**	NOTE: This module is a conversion of routines found in SSUBS.B3
**		and CLIENT.B32.  There are edit histories there

**	X-18	SFW		Steve Wride			26-JUN-20
**              Added device EIA0 to the device list

**	X-17	TGC		Tom Carr			03-JUN-19
**		In preparation for the switch to V5.7 of the DECC compil
**		which does alot more code checking, change the return ty
**		for the routine return type from int to void, since nothi
**		was ever returne

**      X-16    BWK             Barry Kierstein                 19-Dec-19
**              Added devices FAA0, FRA0, FWA0, ERA0 and EWA0 to the devi
**              table list, and made the while statement that processes t
**              list a bit cleare

**      X-15    BWK             Barry Kierstein                 18-Dec-19
**                  When modifications were made to access the lock manager 
**              S2 space, a day-one bug was found.  Turns out that in WINDOW.
**              the routine set_window_title was inserting a '\0' in the by
**              just outside the title buffer.  This now was the low byte 
**              the return address of the function, so that the return fr
**              AMDS$StartNodeWindow in routine DisplayActivateAction d
**              not resume program execution where expected, but in the ca
**              above that starts the single disk windo
**                  This took a LONG time to find, and in an effort not to 
**              bitten by overrunning sprintf statements, double the leng
**              of each temporary buffer that sprintf uses as well as f
**              the error in setting the byte just outside the title buffe
**              Not the greatest solution, but the easiest to do witho
**              a major examination of each buffe
**                  Ironically, trying to fix the buffer overrun by terminati
**              the title string with a '\0' is really not fixing the probl
**              as it ignores what other bytes have been run over by the sprin
**              in the first plac
**                  Each buffer that has been doubled in size was done so 
**              adding "2*" to the dimension (e.g. 64 -> 2*64), so that t
**              changes are easy to find.  For the length of the title buffer
**              a new constant was introduced so that the string truncati
**              statements would work on the intended byt

**	X-14	JAF0956		John Ferlan			17-Feb-19
**		Fix trnlnm itemlists to be initialized and after the trnl
**		call to make the string returned end with a 0....  trnl
**		doesn't do that for you... was setting the amdscomm timeo
**		interval to very large interval

**	X-13	JAF0945		John Ferlan			20-Jan-19
**		Modify printf's for memory allocation - also during debu
**		period add check if our allocation will be too large f
**		the "bigmem" or "biggermem" flags... would we be 'truncatin
**		and masking i

**	X-12	JAF0921		John Ferlan			29-Nov-19
**		Make access to mem_list queues (insque/remque) be synch
**		Also add a routine (mem_dump) to help debug memory leak

**	X-11	JAF0859		John Ferlan			12-Sep-19
**		Make some changes based on feedback from McCabe's tool
**		Also, add code to get system page size and place that val
**		in the page_size field (rather than the previous hardcoded 51

**	X-10	JAF0817		John Ferlan			01-Jul-19
**		On mem_free force min block size of 12!  If not, then pri
**		an error and punt/return.  We've overwritten somethin

**	X-9	JAF0813		John Ferlan			27-Jun-19
**		In mem_free check for badbloadr as error return from lib$free_
**		If present, don't signal it.. There are just too many combi
**		ations that could cause the error to worry about... We'l
**		keep the extra memory (or if we've already deleted, th
**		it doesn't matter anyways!

**	X-8	JAF0793		John Ferlan			03-Jun-19
**		Change the way the global section is used.  Make sure th
**		data within the buffer is aligned on longword or quadwo
**		boundaries based on architecture type (at build time).  Th
**		removes the need for some fields.

**	X-7	JAF0780		John Ferlan			26-May-19
**		Forgot to add code to copy good device name in translati
**		of amds$device logica

**	X-6	JAF0774		John Ferlan			09-May-19
**		Trap some memory bugs and ignore a couple others.

**	X-5	JAF0764		John Ferlan			02-May-19
**		Fix bug in get_dl_chan, where return status was not returne

**	X-4	JAF0757		John Ferlan			20-Apr-19
**		Changes for VCI...  Split out P2 buffer fill routine so th
**		RMCP can call it separately... Modify channel startup t
**		remove any RMCP dependancies... such as print out routine

**		JAF0758		John Ferlan			20-Apr-19
**		Changes for AXP console...  The $CRMPSC service requires t
**		page size for AXP's to be a pagelet not a page...  So don
**		need to create/map the file based on different page sizes.


**	X-3	JAF0747		John Ferlan			09-Feb-19
**		Change the term ethernet to LAN, where appropriatee... Al
**		references to hardware and physical addressing, so tha
**		hardware and physical refer to the 08-00-2b and use DECn
**		instead of what was physical.

**	X-2	JAF0715		John Ferlan			17-Nov-19
**		Make DLLIST and MEM_LIST be static structur
**		Only start one channel when use amds$comm process in dl_sta

**	X-1	JAF0708		John Ferlan			10-Nov-19
**		New module, a bliss to c port of SSUBS.B32 and CLIENT.B32. 
**		With some changes made for continuous improvemen

**		Added two new adapters to the list.. ICA0 and IRA
**





**  INCLUDE FIL




 * This should be the only include file.  All other inclusions are do
 * within the context of this fi
 
#include "src$:sprcdecls.h
#include "src$:nmadef.h"		/*  NMA definitions 



**  TYPEDE






**  TABLE OF CONTEN



 * Shared global section handling routin
 
extern int	util$alloc_circ_buffer (
extern int 	util$circ_bytes_used (
extern int 	util$circ_bytes_free (
extern int 	util$put_circ (
extern int 	util$get_circ (

extern int AMDS$if_true (
extern int AMDS$lnm_getint_value (

static int get_dl_chan ();		/* Open channel to the datalink 
extern void AMDS$Fill_P2 ();		/* Fills the P2 desc with data 
extern int AMDS$Check_amdsdevice_log();	/* Checks AMDS$DEVICE logical valid 
extern int AMDS$startup_dl ();		/* Start the datalink channel... 
extern void report_we_are_lost (

extern void	init_mem_queues ();	/* Initialize the memory queues 
static void	list_mem_queues ();	/* Debug routine 
extern int	mem_alloc (
extern void	mem_free (



**  EXTERNAL REFERENC


extern CSDB *console_block




**  MACRO DEFINITIO


#define MAX_CHANS 9	/* We'll support up to 8, with the ninth entry null 

#define MIN_BLOCK_SIZE AMEM$K_LENGTH + 12    /* 12 is fl/bl/siz/typ/subtyp 
#define MAX_BLOCK_SIZE 4747		     /* A guess... 



 * The following constants are used for alignment in the global section buffe

 * For VAX we use longword alignment, since it's a performance enhance

 * For AXP we use quadword alignment, since it's part of the architectur
 
 * Also on AXP systems, we've had read/write ordering problems between th
 * two processes (console and AMDS$COMM
 
#if defined(VAX) || defined(__VAX) || defined (vax) || defined (__va
#define CBF_ADD	
#define CBF_ALIGN	0xFFFFFFF
#define CBF_MIN		4	/* Minimum bytes that have to be there... 
#el
#define CBF_ADD	
#define CBF_ALIGN	0xFFFFFFF
#define CBF_MIN		8	/* Minimum bytes that have to be there... 
#end


**  DECLARATIO


static int virpagcn
static int page_siz
static int acount = 
static int dcount = 
#if DEB
static int lastallocaddr = 
#end
struct MEMLI

    int	mem_q_f
    int	mem_q_b
    int	mem_q_siz
    int	mem_q_le
    int mem_q_ma
    int mem_q_loc
};         
static struct MEMLIST mem_list[ AMEM$K_INVALID ] = {       
		0, 0, AMEM$K_SML_Q_SIZ, 0, AMEM$K_SML_Q_MAX, 
		0, 0, AMEM$K_MED_Q_SIZ, 0, AMEM$K_MED_Q_MAX, 
		0, 0, AMEM$K_LGE_Q_SIZ, 0, AMEM$K_LGE_Q_MAX, 
		0, 0, AMEM$K_XLG_Q_SIZ, 0, AMEM$K_XLG_Q_MAX, 
		0, 0, AMEM$K_XXL_Q_SIZ, 0, AMEM$K_XXL_Q_MAX, 
		0, 0, AMEM$K_ECM_Q_SIZ, 0, AMEM$K_ECM_Q_MAX, 
		0, 0, AMEM$K_HUG_Q_SIZ, 0, AMEM$K_HUG_Q_MAX, 
		

static int AMDS$gblsec_sysgbl = 
static int AMDS$gblsec_group = 
static  char errstr[132]; /* module level error string 
                              
struct DLLIS

    int	devlist_inde
    int	dl_cha
};         

static int	rm_active = 0;		/* Boolean for using RMA0 as channel 
static int 	dl_chan_count = 0;	/* Number of datalink channels...
static struct	DLLIST	dl_chanlist [MAX_CHANS] = {/* Initialize our channel 
			0, 0,		/* list. This is basically an array 
			0, 0,		/* of all available datalink 
			0, 0,		/* channels...  Some systems 
			0, 0,		/* support up to 8 adaptors of 
			0, 0,		/* one kind...  Not sure if that 
			0, 0,		/* means 8 DEBNI plus 8 DEMNA plus 
			0, 0,		/* 8 DEMFA... But if we have more 
			0, 0,		/* 8 we'll punt anyways... 
			0, 0



 * Current list of supported devices.  Ensure that the " " is the la
 * 'device' in the list, as it is the marker to end the list.. I.e. if 
 * get here, they're aren't anymore LAN devices to connect t
 */                            
static char	*devlist [] = { "RMA0",		/* Ourself? only when comm 
				"AMDS$DEVICE",	/* Logical name 
				"FXA0",		/* FDDI 
				"FCA0
				"ECA0",		/* Turbochannel to LAN 
				"ICA0",		/* Turbochannel to Token Ring 
				"IRA0",		/* EISA to Token Ring 
				"XEA0",		/* All below are Ethernet 
				"XQA0
				"EFA0
				"ETA0
				"ESA0
				"EXA0
				"EZA0
                                "FAA0
                                "FRA0
                                "FWA0
                                "ERA0
                                "EWA0
                                "EIA0
				" "


extern int util$alloc_circ_buffer ( int size, int *bufaddr
				    struct dsc$descriptor *name

**

** FUNCTIONAL DESCRIPTI

**	This routine will allocate a circular buffer for send and the consol
**	to use when communicating.  This buffer will be a global sectio

** FORMAL PARAMETE

**   size is the byte size of the data buffer to allocate, and should 
**        at least as large as the largest data message + 1.  Passed b
**	 reference.                         
**                                             
**   buf is the address of a variable to receive the address of the fir
** 	byte in the buffer (low byte of the SIZE longword

**   name (optional) is a global section name passed by descriptor.  
**	supplied, the circular buffer will be allocated in a global section s
**	named.  At this time this argument is require

** IMPLICIT INPUTS	Non

** IMPLICIT OUTPUTS	Non

** RETURN VAL
**       
**   status is returned stat

** SIDE EFFECTS:	Non

**


    int inadr[2];	/* params to global section routine 
    int retadr[2]

    int numpgs = ( size + CBF$K_HEADER_LENGTH + 512 - 1 ) / 51
	
    CBF *bu

    int gblsec_fl = ( SEC$M_GBL | SEC$M_WRT | SEC$M_PAGFIL 
    int statu

    $DESCRIPTOR ( sysgbl_lnm, "AMDS$GBLSEC_SYSGBL" 


    /
     * Check for which type of global section group or syst
     
    if ( AMDS$if_true ( &sysgbl_lnm )
   
	gblsec_fl |= SEC$M_SYSGB
	AMDS$gblsec_sysgbl = 
   
    el
	AMDS$gblsec_group = 1;	/* default 
                    
    
     * Get some memory for the global secti
     
    status = sys$expreg ( numpgs,  &inadr[0], 0, 0
    if ( status != SS$_NORMAL
	return ( status 
              
    
     * Create the mapped secti
     
    status = sys$crmpsc ( &inadr[0], &retadr[0], PSL$C_USER, gblsec_f
			  name, 0, 0, 0, numpgs, 0, 0, 0 
    buf = ( CBF *) inadr[0
    if ( status == SS$_CREATED
   
	buf->CBF$L_SIZE		= ( numpgs * 512 ) - CBF$K_HEADER_LENGT
	buf->CBF$L_IN		= 
	buf->CBF$L_OUT		= 
   
    *bufaddr = bu
    return ( status 




extern int util$circ_bytes_used ( CBF *buf

**
** FUNCTIONAL DESCRIPTIO
*
**   	Returns number of bytes currently used in a circular buffe
*
**	NOTE: This call is completed regardless of the fact that someon
**		may be currently changing the pointers..  All this ca
**		is designed to do is to return the number of bytes current
**		not in use.  It is up to the call process to figure out i
**		it can do its operatio

** FORMAL PARAMETER
*
**   	buf  -  Pointer to CBF structu
**  
** IMPLICIT INPUTS:     No
*
** IMPLICIT OUTPUTS:	No
*
** ROUTINE VALU
*
**   	Count of bytes Use
*
** SIDE EFFECTS:	No
*
**


    int nbyte
    nbytes = buf->CBF$L_IN - buf->CBF$L_OU
    if ( nbytes >= 0
	return ( nbytes 
    el
	return ( buf->CBF$L_SIZE + nbytes 



extern int util$circ_bytes_free ( CBF *buf

**
** FUNCTIONAL DESCRIPTIO
*
**   	Returns number of bytes currently free in a circular buffe
*
**	NOTE: This call is completed regardless of the fact that someon
**		may be currently changing the pointers..  All this ca
**		is designed to do is to return the number of bytes current
**		not in use.  It is up to the call process to figure out i
**		it can do its operatio

** FORMAL PARAMETER
*
**   	buf  -  Pointer to CBF structu
*
** IMPLICIT INPUTS:     No
*
** IMPLICIT OUTPUTS:	No
*
** ROUTINE VALU
*
**   	Count of bytes FRE
*
** SIDE EFFECTS:	No
*
**


    int nbyte
    nbytes = buf->CBF$L_OUT - buf->CBF$L_I
    if ( nbytes > 0
	return ( nbytes 
    el
	return ( buf->CBF$L_SIZE + nbytes 

                                               

extern int util$put_circ ( int nbyt, CBF *buf, unsigned char *data

**

** FUNCTIONAL DESCRIPTI

**	The PUT process uses this routine to write data to the buffe

**   	If there are at least nbyt+1 bytes in the buffer, UTIL$PUT_CIRC copie
**	them into the buffer and advances the IN pointer appropriately.
**	Normal return with SS$_NORMAL statu

**   	If there is not enough free space in the buffer, UTIL$PUT_CIRC retur
**   	the status code SS$_TOOMUCHDATA and does not change the pointer

**   	When PUT gets NBYT bytes of data to give to GET
**   	PUT checks to see if there is room in the buffer.  This can happen i
**   	eithe
**   	    (P1)  OUT > IN, in which case there are exactly OUT-IN bytes fr

**	    (P2)  OUT <= IN, in which case there are exactly SIZE+OUT-IN byt
**			      fr

**   	It is important that there be MORE THAN NBYT BYTES FREE.  You are no
**	allowed to exactly fill the buffer, since that would result in th
**	pointers being advaced until OUT=IN, which indicates an empty buffe
**	This results in the following alternative

**   	    If there are more than NBYT bytes available, store the NBYT byte
**	    in the buffer and advance IN by NBYT.  It may GET two move oper
**	    tions to store the data.
*
**   	    Do not store anything in IN until all the move operations are don
**	    then store only the correct final value

**   	    If there are NBYT or less bytes available, stall until GET remove
**	    some of the data from the buffe

**   	IN is changed only by the PUT proces

** FORMAL PARAMETE

**   	nbyt	Number of bytes of data to send (passed by reference

**   	buf 	The address of the first byte of the circular buffe

**  	data 	The address of the first byte to be written to the buff

** IMPLICIT INPUTS	Non

** IMPLICIT OUTPUTS	Non

** RETURN VAL
**                              
**	Status (SS$_NORMAL or SS$_TOOMUCHDAT
**                                           
** SIDE EFFECTS:	Non


**
*/                                    

    int fre_bytes, end_byt
    unsigned char *add

    
     * If the number of bytes to put into the gblsec is less than our minim
     * value, return an error... This value is architecture specific dependi
     * on whether or not we're doing long or quad word alignmen
     
    if ( nbyt < CBF_MIN
	return ( SS$_BADPARAM 

    
     * This should never hold true, since we check to make sure we have 
     * least room enough for two ECM's in the global section before ever tryi
     * to write to it, but one never know
    
     * Note: Gotta have the >= because, if exact then IN would equal OU
     *		which would mean there's nothing in ther
     */    
    fre_bytes = util$circ_bytes_free ( buf 
    if ( nbyt >= fre_bytes
   
	return ( SS$_TOOMUCHDATA 
   

    /*               
     * See if one contiguous chunk or two separate chunks.  Since we kn
     * we already have room in the buffer, all we need do is ask if the
     * is space from IN to LIMIT -- we can't overrun OU
     
    if ( ( nbyt + buf->CBF$L_IN ) < buf->CBF$L_SIZE
    {    
	addr = buf->CBF$A_DATA + buf->CBF$L_I
	memcpy ( addr, data, nbyt 

	
	 * Let's figure out where the "next" buffer will appear assuming w
	 * desire longword alignmen
	 
	end_byte = ( nbyt + CBF_ADD ) & CBF_ALIGN
	buf->CBF$L_IN += end_byt
   
    el
   
	int segsiz = buf->CBF$L_SIZE - buf->CBF$L_I
                  
	
	 * Move in two chun
	 
	addr = buf->CBF$A_DATA + buf->CBF$L_I
	memcpy ( addr, data, segsiz 
	addr = buf->CBF$A_DATA;	/* Move pointer 
	memcpy ( addr, &data[segsiz], ( nbyt - segsiz ) 
	
	 * Let's figure out where the "next" buffer will appear assuming w
	 * desire longword alignmen
	 
	end_byte = ( (nbyt-segsiz) + CBF_ADD ) & CBF_ALIGN
	buf->CBF$L_IN = end_byt
   
    return ( SS$_NORMAL 


extern int util$get_circ ( int nbyt, CBF *buf, unsigned char *data

**
**  FUNCTIONAL DESCRIPTIO

**   	If there are at least nbyt bytes of data in the buffer, UTIL$GET_CI
**   	copies them from the buffer and advances the OUT pointer appropriatel
**   	Normal return with SS$_NORMAL statu

**   	If there are not at least nbyt bytes of data in the buffer, retur
**   	the status code SS$_NODATA and do not change the pointer

**   	If the PUT and GET processes are using fixed-size messages th
**   	one call to UTIL$GET_CIRC is all that is required to read a messag
**   	If instead the message size is variable then some convention will 
**   	needed so that GET will be able to determine the number of bytes 
**   	request, lest an abutting message be read as part of one reques
**   	This can be accomplished by various PUT/GET conventions.  For examp
**   	PUT could store a longword of length before the message, and GET cou
**   	make two calls to UTIL$GET_CIRC, first a four-byte "length" reques
**   	then a second "data" request, passing the returned "length" valu

**  FORMAL PARAMETERS:

**   	nbyt	= Number of bytes to be read from the buff

**   	buf 	= The address of the first byte of the circular buffe

**   	data 	= The address of where the buffer contents are to be writt

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:          

**	Status. (SS$_NORMAL and SS$_NODATA

**  SIDE EFFECTS:       no
**


    int usd_bytes, end_byt
    unsigned char *add

    
     * If the number of bytes to take out of the gblsec is less than our minim
     * value, return an error... This value is architecture specific dependi
     * on whether or not we're doing long or quad word alignmen
     
    if ( nbyt < CBF_MIN
	return ( SS$_BADPARAM 

    
     * The following should never be true, but we must check
     */       
     usd_bytes = util$circ_bytes_used ( buf 
     if ( usd_bytes < nbyt
	return ( SS$_NODATA 

    
     * If we don't need to "wrap" to the head and we can get in one chunk.
     
    if ( ( buf->CBF$L_SIZE - buf->CBF$L_OUT ) >= nbyt
   
	addr = buf->CBF$A_DATA + buf->CBF$L_OUT
	memcpy ( data, addr, nbyt 

	
	 * Let's figure out where the "next" buffer will appear assuming w
	 * desire longword alignmen
	 
	end_byte = ( nbyt + CBF_ADD ) & CBF_ALIGN
	buf->CBF$L_OUT += end_byte;   
   
    el
   
	int segsiz = buf->CBF$L_SIZE - buf->CBF$L_OU

	
	 * Otherwise, we need to move the data in two chunks, first t
	 * "end" of the buffer, then start at the head again.
	 
	addr = buf->CBF$A_DATA + buf->CBF$L_OUT
	memcpy ( data, addr, segsiz 
	addr = buf->CBF$A_DAT
	memcpy ( &data[segsiz], addr, ( nbyt - segsiz ) 
	
	 * Let's figure out where the "next" buffer will appear assuming w
	 * desire longword alignmen
	 
	end_byte = ( (nbyt-segsiz) + CBF_ADD ) & CBF_ALIGN
	buf->CBF$L_OUT = end_byt
   
    return ( SS$_NORMAL 




extern int AMDS$if_true ( struct dsc$descriptor *ldesc

**
**  FUNCTIONAL DESCRIPTIO

**	This routine try's to translate the logical name provided as inp
**	and returns a status of 1 (TRUE) if the first character of t
**	equivalence name is a T(rue), E(nable), Y(es) or 1.  Otherwise
**	0 (FALSE) is returne

**  FORMAL PARAMETERS:

**	ldesc		Pointer to a string descriptor of the logical nam

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:          

**		1 or 0 true/fal

**  SIDE EFFECTS:       no
**


    int status;                

    int mask = LNM$M_CASE_BLIN
    char t_buffer[32
    int retlen = 
    struct _itmlst trnlnm_itmlst[] =
	32, LNM$_STRING, &t_buffer, &retle
	0, 0, 0, 0

    $DESCRIPTOR ( lnm_tbl, "LNM$FILE_DEV");               

    t_buffer[0] = 
    status = sys$trnlnm ( &mask, &lnm_tbl, ldesc, 0, trnlnm_itmlst 
    t_buffer[retlen] = 
    if ( status == SS$_NORMAL
   
	
	 * Upcase the first charact
	 
	if ( t_buffer[0] >= 'a' && t_buffer[0] <= 'z'
	    t_buffer[0] -= 0x2

	
	 * Check for valid affirmative respons
	 
	if ( t_buffer[0] == 'T' ||	/* True 
	     t_buffer[0] == 'Y' ||	/* Yes 
	     t_buffer[0] == 'E' ||	/* Enable 
	     t_buffer[0] == '1' )	/* 1 

	    return ( 1 
   

    return ( 0 



            
extern int AMDS$lnm_getint_value ( struct dsc$descriptor *ldesc

**
**  FUNCTIONAL DESCRIPTIO

**	This routine will attempt to parse the incoming logical name a
**	return a value if the logical name is parsed.

**  FORMAL PARAMETERS:

**	ldesc		Pointer to a string descriptor of the logical na

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:          

**		Integer value for logical name translatio

**  SIDE EFFECTS:       no
**


    int statu
    int ret_value = 

    int mask = LNM$M_CASE_BLIN
    char t_buffer[32
    int retlen = 
    struct _itmlst trnlnm_itmlst[] =
	32, LNM$_STRING, &t_buffer, &retle
	0, 0, 0, 0
                
    $DESCRIPTOR ( lnm_tbl, "LNM$FILE_DEV"

    t_buffer[0] = 
    status = sys$trnlnm ( &mask, &lnm_tbl, ldesc, 0, trnlnm_itmlst 
    t_buffer[retlen] = 
    if ( status == SS$_NORMAL ) ret_value = atoi ( t_buffer 
    return ( ret_value 
  



extern void report_we_are_lost ( int errlen, char *errstr



**
**  FUNCTIONAL DESCRIPTIO

**	This is the central repository to report all types of cannotgethere
**	and shouldnotgethere'

**  FORMAL PARAMETERS:

**	errlen		Length of error stri

**	errstr		Error stri

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**
*/                                    

    lib$signal ( AMDS$_CANNOTGETHERE, 2, errlen, errstr 



static int check_rm_active 

**
** FUNCTIONAL DESCRIPTION                                     

**	This function returns 1 if the driver is available to be use
*
** INPUT


** OUTPUT

**	Status = From the $ASSIGN servi

**


    int		dev_desc [2] = {0, 0
    int		chan;				/* channel 
    short int 	status; 			/* status 
    int		retval = 0;			/* Assume not... 

    
     * Assigna changle to rmdriver.
     
    dev_desc [0] = strlen(devlist[0]
    dev_desc [1] = devlist[0
    status = sys$assign ( &dev_desc, &chan, 0, 0

    /*   
     * If we are RMA0, we need to check if we're enabled as wel
     
    if ( status == SS$_NORMAL
   
	short int	iosb [4];	/* Standard I/O Status Block 
	int dvi_devst
	itmlst	getdvi_itmlst [] = { /* Define GETDVI item list 
		4, DVI$_DEVSTS, &dvi_devsts, 0
		0, 0, 0, 0

	status = sys$getdviw ( 	0, chan, &dev_desc, getdvi_itmlst
				iosb, 0, 0, 0
	
	 * If we are not started, then deassign chann
	 
	if ( ( dvi_devsts & RM$M_READY ) == 0
	    sys$dassgn ( chan 
	el
	{     
	    dl_chanlist[ dl_chan_count ].devlist_index = 
	    dl_chanlist[ dl_chan_count ].dl_chan = cha
	    dl_chan_count+
	    rm_active = 
	    retval = 

   
    return ( retval 



static int get_dl_chan (

/
**
** FUNCTIONAL DESCRIPTION                                     

**	This routine will attempt to open up a channel to all foun
**	adaptors.
*
** INPUT

**	non

** OUTPUT

**	Status = From the $ASSIGN servi

**
*/                                   

    int 	idx;				/* Loop 
    int		userdefined = 0;		/* AMDS$DEVICE defined/used 
    int		chan;				/* channel 
    int 	status = SS$_NORMAL;		/* status 

    int		dev_desc [2] = {0, 0
    int		length
    char	lan_buf[4];		/* 4 bytes 
    int		lan_desc[2] = {0,0};	/* Descriptor to hold trnlnm name 


    
     * If RMA0 is not active, then we need to find a datali
     * with whom to communicate our protoco
     
    if ( !check_rm_active()
   
	
	 * Next check to see if AMDS$DEVICE is defined.  If so, ensure th
	 * the device it points to is cool...  If cool then we're going 
	 * set our channel to it; otherwise, we'll start looking in th
	 * channel list at the 3rd entry (rma0=1,amds$device=
	 
	status = AMDS$Check_amdsdevice_log ( lan_buf, &length 
	if ( status == SS$_NORMAL

	    
	     * If we have a valid AMDS$DEVICE we know that we've alread
	     * assigned a channel to it (temporarily at least) so no nee
	     * to check that... just get the channel, populate the structur
	     * and get out of her
	     
	    dev_desc [0] = strlen(devlist[1]
	    dev_desc [1] = devlist[1
	    sys$assign ( &dev_desc, &chan, 0, 0
	    dl_chanlist[dl_chan_count].devlist_index = 
	    dl_chanlist[dl_chan_count].dl_chan = cha
	    dl_chan_count+

	el

	    /*       
	     * Loop through trying to assign a channel to the device.  If fou
	     * save the channel's information in our global channel list, an
	     * try the next channel until we've exhausted our lis
	    
	     * Note: If the AMDS$DEVICE logical is defined
	     *		only use that channel.
	     
	    idx = 2;	/* NB: We start at the 3rd entry... 
	    while (*devlist[ idx ] != ' ' && dl_chan_count < MAX_CHAN
	   
		
		 * Set up a descriptor for the sys$assign servic
		 * to try to make the connection t
		 
		dev_desc [0] = strlen(devlist[ idx ]
		dev_desc [1] = devlist[ idx 
		status = sys$assign ( &dev_desc, &chan, 0, 0

		
		 * If found then populate structure.
		 
		if ( status == SS$_NORMAL
	
		    
		     * Fill in our array for datalink channels.
		     
		    dl_chanlist[dl_chan_count].devlist_index 	= id
		    dl_chanlist[dl_chan_count].dl_chan	  	= cha
		    dl_chan_count+
	
		
		 * We expect SS$_NOSUCHDEV, signal others.
		 
		else if (status != SS$_NOSUCHDE
		    lib$signal ( status 
		
		 * Increment loop counter, and try next chann
		 
		idx+
	   

   

    
     * If we didn't find one, then signal a messa
     
    if ( dl_chan_count == 0 ) status = AMDS$_NODATALIN
    else status = SS$_NORMA

    return ( status );                                          




int AMDS$Check_amdsdevice_log ( char *lanname, int *le

**++                                                       
** FUNCTIONAL DESCRIPTI

**	This routine will check the validity of the AMDS$DEVICE logica
**	If bad, then it errors ou

** INPU

**	lanname = Name of the lan device to be returned if AMDS$DEVICE 
**		  a define
**	len = Length of the name (4 or 

** OUTPU

**	status 
**


    int		status = SS$_NORMAL;	/* Status 
    short int	iosb [4];		/* Standard I/O Status Block 


    char	t_buffer[80];		/* Buffer to hold our name 
    int		trn_retlen = 0;		/* Return length of descriptor 
    itmlst	trnlnm_itmlst [] = {	/* Define our trnlnm item list 
	80, LNM$_STRING, t_buffer, &trn_retle
	0, 0, 0, 0
    $DESCRIPTOR ( lnm_tbl, "LNM$FILE_DEV");               
    $DESCRIPTOR ( trn_name, "AMDS$DEVICE"

                       
    
     * Translate the logical... if successful and we have something, th
     * let's check it out.
     
    t_buffer[0] = 
    status = sys$trnlnm (0, &lnm_tbl, &trn_name, 0, trnlnm_itmlst
    t_buffer[trn_retlen] = 
    if ( status == SS$_NORMA
    {
	if ( trn_retlen == 4

	    int assign_statu
	    int cha

	    
	     * We check the validity by attempting to assign a channel to t
	     * device.  If successful, then we have a good device, otherwi
	     * set the return status to the status received and let the call
	     * take the appropriate actio
	     
	    assign_status = sys$assign ( &trn_name, &chan, 0, 0
	    if ( assign_status != SS$_NORMAL 
	   
		status = assign_statu
		lib$signal ( AMDS$_BADLANADR, 2, trn_retlen, t_buffer 
	   
 	    el
	   
		sys$dassgn ( chan 
		*len = trn_retle
		
		 * Capitalize.
		 
		if ( t_buffer[0] >= 'a' && t_buffer[0] <= 'z
		    t_buffer[0] -= 0x2
		if ( t_buffer[1] >= 'a' && t_buffer[1] <= 'z
		    t_buffer[1] -= 0x2
		if ( t_buffer[2] >= 'a' && t_buffer[2] <= 'z
		    t_buffer[2] -= 0x2
		strncpy ( lanname, t_buffer, 4
	   

	el

	    
	     * If by chance the logical was defined, but not to something tha
	     * is defined as "legal", we need to state 
	     
	    lib$signal ( AMDS$_UNKSTYLE, 2, trn_retlen, t_buffer 

   


    return ( status 



  
extern void AMDS$Fill_P2 ( int *p2desc, int num_buffers


**++                  
**               
** FUNCTIONAL DESCRIPTI
**     
**	This routine will fill in the data in the P2 Buffer that needs 
**	be sent to the datalink to start the DECamds protocol on the wir

**	This routine is shared by RMCP and AMDS$CO

** 	The P2 buffer sent to our QIO to initialize the particular LA
**	driver about what type of information is going to be sent over th
**	wire.  The buffer must be set up in the format o

**	PARAMETER ID (word
**	Longword Value or Counted Stri

** 	All values shown in I/O User's Guide Part II (middle of chaper 

** 	This structure is set up to let us use a private protocol across t
** 	wire in the LAN/802 Extended Format.

** 	Required parameters ar

** 	NMA$C_PCLI_FMT -> Packet Format (802
** 	NMA$C_PCLI_PID -> Protocol Identifier (08-00-2B-80-48

** 	Error parameters ar

** 	NMA$C_PCLI_PTY -> Protocol Typ
** 	NMA$C_PCLI_SAP -> 802 formap SAP (Service Access Poin
** 	NMA$C_PCLI_ACC -> Protocol Access Mo
** 	NMA$C_PCLI_DES -> Shared Protocol Destination Addre
** 	NMA$C_PCLI_PAD -> Use of message size field on X-mit/receive messag
** 	NMA$C_PCLI_SRV -> Channel Servi
** 	NMA$C_PCLI_GSP -> Group S

** 	Optional parameters are: (default or our value in parens afte
**	description (Asterisked (*) values are ones we us

**     *NMA$C_PCLI_BFN -> Number of buffers to preallocat
** 	NMA$C_PCLI_BSZ -> Device Buffer Size ( max of 150
**     *NMA$C_PCLI_BUS -> Max. allowable channel receive buff. siz
** 	NMA$C_PCLI_CON -> Controller Mode (NMA$LINCN_NO
** 	NMA$C_PCLI_CRC -> CRC Generation State (NMA$C_STATE_O
**     *NMA$C_PCLI_DCH -> Data Chaining State (NMA$C_STATE_OF
** 	NMA$C_PCLI_EKO -> Echo Mode (NMA$C_STATE_OF
**	NMA$C_PCLI_ILP -> Internal Loopback Mode. (NMA$C_STATE_OF
**     *NMA$C_PCLI_MCA -> Multicast Address (NMA$C_LINMC_SET: 09-00-2B-02-01-0
**	NMA$C_PCLI_MLT -> Multicast Address State (NMA$C_STATE_OF
**     *NMA$C_PCLI_PHA -> Physical Port Address (current/hdwr addres
**	NMA$C_PCLI_PRM -> Promiscuous mode (NMA$C_STATE_OF
**     *NMA$C_PCLI_RES -> Restart. (NMA$C_LINRES_EN


** INPUT

**	p2desc		Pointer to a two longword buffer.
**			It is assumed that the second longword contains
**			buffer of at least 100 byte
**	num_buffers	Number of buffers for DL to prealloca

** OUTPUTS:                                    

** 	The filled buffe
*
**


/*

 * Initialize the P2 buffer.  The P2 buffer is the buffer sent to t
 * datalink to start up DECamds's protocol on the LA

 * The NMA$ constants and types used below are defined in the I/O User'
 * Guide in the chapter discussing LAN/802 Device Driver
 * The NMA$ constants come from the NMADEF.SDL in the <NCP> VMS facilit
 * They are defined in LIB, but I couldn't find the VAXC equivalent 
 * LIB.MLB or LIB.L32 so I just copied the NMADEF.SDL out of the <NC
 * facility and use it...

 
    int		bufsiz = 
    long	*buflpos;	/* Pointers into quota list for long access *
    short int	*bufwpos;	/* Pointers into quota list for word access *
    char	*bufcpos;	/* Pointers into quota list for char access *


 * This has got to be the *UGLIEST* way to set up the buffer, but it w
 * the only way it worked correctl
 
    bufwpos = (short int *)p2desc[1];	/* Address of our buffer 


 * Start of word parameter_id / longword value pai
 
    *bufwpos++ 	= NMA$C_PCLI_FMT;	/* Packet Format 
    buflpos 	= bufwpo
    *buflpos++	= NMA$C_LINFM_802E;	/* 802E 
    bufsiz	+= 

    bufwpos	= buflpo
    *bufwpos++	= NMA$C_PCLI_BFN;	/* Number of buffers to preallocate 
    buflpos	= bufwpo
    *buflpos++	= num_buffers;		/* From logical translation 
    bufsiz	+= 

    bufwpos	= buflpo
    *bufwpos++	= NMA$C_PCLI_DCH;	/* Data Chaining State 
    buflpos	= bufwpo
    *buflpos++	= NMA$C_STATE_OFF;	/* OFF - (default) 
    bufsiz	+= 

    bufwpos	= buflpos;		/* NOTE: Only valid > V5.4-3 
    *bufwpos++	= NMA$C_PCLI_CCA;	/* Can Change Address 
    buflpos	= bufwpos;                                   
    *buflpos++	= NMA$C_STATE_ON;	/* ON 
    bufsiz	+= 

    bufwpos	= buflpos;                                   
    *bufwpos++	= NMA$C_PCLI_BUS;	/* Max. Channel Rcv. buffer size 
    buflpos	= bufwpo
    *buflpos++	= AMDS$K_LAN_BUF_SIZ;	/* Use sparcdef defined symbol 
    bufsiz	+= 

    bufwpos	= buflpo
    *bufwpos++	= NMA$C_PCLI_RES;	/* Automatic channel restart 
    buflpos	= bufwpo
    *buflpos++	= NMA$C_LINRES_ENA;	/* Enable automatic restart 
    bufsiz	+= 


 * End of word paremter_id / longword value pai

 * Start of word parameter_id / counted character string pair
 
    bufwpos	= buflpo
    *bufwpos++	= NMA$C_PCLI_PHA;	/* Physical Port Address 
    *bufwpos++	= 2;              	/* Counted byte string 
    *bufwpos++	= NMA$C_LINMC_SD
    bufsiz	+= 


    *bufwpos++	= NMA$C_PCLI_MCA;	/* Multi-cast Address 
    *bufwpos++	= 8;			/* Counted byte string... 
    *bufwpos++	= NMA$C_LINMC_SET;	/* Set the multicast address 
    bufcpos	= bufwpo
    *bufcpos++	= 0x09;			/* 09-00-2B-02-01-09 
    *bufcpos++	= 0x0
    *bufcpos++	= 0x2
    *bufcpos++	= 0x0
    *bufcpos++	= 0x0
    *bufcpos++	= 0x0
    bufsiz	+= 1

    bufwpos	= bufcpo
    *bufwpos++	= NMA$C_PCLI_PID;	/* Protocol ID 
    *bufwpos++	= 5;			/* Counted string 
    bufcpos	= bufwpo
    *bufcpos++	= 0x08;			/* 08-00-2B-80-48 
    *bufcpos++	= 0x0
    *bufcpos++	= 0x2
    *bufcpos++	= 0x8
    *bufcpos++	= 0x4
    bufsiz	+= 

    p2desc[0] = bufsiz;     /* Number of bytes of actual data to be sent 

                                       




extern int AMDS$startup_dl ( int num_buffers, int from_com
			     int *returned_chan

**++                  

** FUNCTIONAL DESCRIPTI
**     
**	This routine will attempt to start up datalink protocol on the list 
**	valid LAN adapter types.  This routine is to be shared by RM
**	and AMDS$COMM as a single point of entry for both.  The major differen
**	between the two is that if we are being called from AMDS$COMM then 
**	need to check if RMA0 is active and if so use it since it will be t
**	conduit for all our message

** INPUT
**          
**	num_buffers	Number of buffers to be preallocated by datalin
**	from_comm	currently unuse
**	returned_chan	Channel to return to calle


** Outputs:                                    

** 	returned_chan 	= Value to return our channel in, if an
** 	status		= Status of success or failu

**            
**


    int i = 0;			/* Loop 
    int statu
    int	nmabuf[100];		/* buffer to fill ... 
    int	nmadsc[2] = {0,&nmabuf};/* NMA buffer descriptor 
    short int stiosb[4

    
     * Find all the available datalink channe
     */        
    status = get_dl_chan ( 
    if ( status != SS$_NORMAL 
   
	*returned_chan = 
	lib$signal ( status 
   
    
     * If we know our channel is the RMA0 channel, then just exit the start
     * isn't going to do anything, the protocol is already start
     
    else if ( rm_active
   
	*returned_chan = dl_chanlist[ 0 ].dl_chan;	/* Set return value 
   
    
     * Otherwise, try to find a line to start up on.
     */                      
    el
    {         
	
	 * Fill in the P2 Buff
	 
	AMDS$Fill_P2 ( &nmadsc, num_buffers 

	
	 * Loop through all valid channels... trying to start the protocol.
	 
	while ( i < dl_chan_count

	    
	     * Issue a QIOW to the particular LAN driver we have a channel t
	     * to set controller mode and start the controller po
	     
	    status = sys$qiow (	0, dl_chanlist[ i ].dl_chan
				IO$_SETMODE | IO$M_CTRL | IO$M_STARTU
				&stiosb, 0, 
				0, nmadsc, 0, 0, 0, 0
	    if ( status == SS$_NORMAL && stiosb[0] == SS$_NORMAL
	   
		brea
	   
	    el
	   
		/
		 * If it's not fatal, then we'll try the next line.
		 * but if fatal, then this will force ex
		 * Also, note that we ensure we don't signal the sam
		 * message twice - the stiosb[0] should be the same a
		 * the status, but I'm not taking any chances.
		 
		lib$signal ( stiosb [0] 
		if ( stiosb [0] != status ) lib$signal ( status )
	   
	    i+


	
	 * Set up return chann
	 
	*returned_chan = dl_chanlist[ i ].dl_cha
   
    return ( status 



extern void init_mem_queues 

**
**  FUNCTIONAL DESCRIPTIO

**	This routien will initialize the various memory lookaside list he
**	pointers...  This gets called once at application startup... Fo
**	both console and com

**  FORMAL PARAMETERS:  no

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**


    int id
    int statu
    struct _itmlst getsyi_itmlst[] =
	sizeof(int), SYI$_PAGE_SIZE, &page_size, 
	sizeof (int), SYI$_VIRTUALPAGECNT, &virpagcnt, 
	0, 0, 0, 0 

    
     * Get and save the system's page_size and the virtual page coun
     
    status = sys$getsyi(0, 0, 0, getsyi_itmlst, 0, 0, 0)
    if ( status != SS$_NORMAL
	lib$signal ( status 

    
     * Initialize the queue headers.. the mem_q_size, mem_q_len, an
     * mem_q_max fields are already filled in.
     
    for ( idx = AMEM$K_SML_Q; idx < AMEM$K_INVALID; idx++
   
	mem_list[idx].mem_q_fl = &mem_list[idx].mem_q_f
	mem_list[idx].mem_q_bl = &mem_list[idx].mem_q_f
   


 
extern void list_mem_queues 

**
**  FUNCTIONAL DESCRIPTIO

**	This routine will list some data about memory queue

**  FORMAL PARAMETERS:  no

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**


    int total = 
    int id
    int av

    printf ( "\nList mem queues... Acount: %d, Dcount: %d, diff: %d
			acount, dcount, acount-dcount 

    for ( idx = AMEM$K_SML_Q; idx < AMEM$K_INVALID; idx++
   
	total += ( mem_list[idx].mem_q_len 
		   ( mem_list[idx].mem_q_size + AMEM$K_LENGTH ) 
	printf ( "\n\tMem qsize: %d, qlen: %d"
			mem_list[idx].mem_q_size, mem_list[idx].mem_q_len 
   

    avg = total / page_siz
    printf ("\nTotal queued pages: %d bytes, : %d pages ", total, avg 
                  
}	  
                                  

extern int mem_alloc ( syze, type, va_alist
    int syz
    int typ
    va_dcl		/* NB: no semicolon 

**
**  FUNCTIONAL DESCRIPTIO

**	This routine handles the crux of all of DECamds' memory allocati
**	using the LIB$GET_VM routine.  It is smart enough to also che
**	for special blocks which are used quite frequently within the cour
**	of running DECamds.  If this routine detects one of these speci
**	blocks it will attempt to REMQUE from a lookaside list of blocks th
**  	have already been used (and deallocated) instead of using the overhe
**	of multiple LIB$GET_VM call

**	It will allocate a buffer of requested size plus an internal head
**	as follow

**		---------------
**		|<size  | FEED |
**		--------------
**		|sbt|typ| size>|
**		--------------

**  FORMAL PARAMETERS:

**	syze		Size of block to be allocat
**	type		Type of block to be allocat
**	subtype		(opt) Any subtype to be placed he

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:          

**	Address of block to be allocat

**  SIDE EFFECTS:       no
**


    va_list a
    int idx;		/* loop 
    int size = syz
    int block_size = -
    int got_bloc
    int statu
    int subtyp
    int big_mem = 0 + ( AMDS$K_BIG_MEM_OK << AMDS$K_BIG_MEM_SHIFT 
    int bigger_mem = 0 + ( AMDS$K_BIGGER_MEM_OK << AMDS$K_BIGGER_MEM_SHIFT 
      
    AMEM *membl
    STD *bl

    if ( size < 0 ) printf ("\n size: %d   <  0!", size

    /*  
     * Did we get a 3rd para
     
    va_start ( ap );	/* start varargs... 
    va_count ( subtype 
    if ( subtype > 2 )	/* More than 2 args? 
	subtype = va_arg ( ap, int );	/* Yep, 3rd is SUBTYPE 
    el
	subtype = 
    va_end ( ap 

    /*                                           
     * Size check... Can only allocate up to max_block_size bytes, unle
     * a special flag or two is set in the left 16 bits... This is done t
     * ensure we know at certain portions of code that we will be allocati
     * a rather large chunk of memory and that's what we want to d
     
    if ( ( size & big_mem ) == big_mem 
   
	
	 * If the size and !big mem is greater than a big mem alloc, we'
	 * have problems just 'cutting' off the size.
	 
	if ( ( size & ~big_mem ) > 0x0000FFFF ) /* 65535 

	    printf ("\n AMDS BIG MEM Alloc masking incorrectly: %d ", size


	size &= 0x0000FFF
   
    else if  ( ( size & bigger_mem ) == bigger_mem
   
	if ( ( size & ~bigger_mem ) > 0x00FFFFFF ) /* 16,777,215 

	    printf ("\n AMDS BIGGER MEM Alloc masking incorrectly: %d ", size

	size &= 0x00FFFFF
   
    else if ( size > MAX_BLOCK_SIZE
    	printf ("\n*** NOBIGMEM... AMDS Alloc HUGE block... size : %d ", size 

    
     * Check to make sure we're less than virtual page count, which happe
     * to be the largest packet we can successfully handle...  If not th
     * is a fatal error.
     
    if ( ( size / page_size ) > virpagcnt
   
	printf ("\n***AMDS$INFO Alloc block pages : %d gtr than virpagcnt: %d
			size/page_size, virpagcnt 
	printf ("\n***AMDS$INFO, increase SYSGEN parameter VIRPAGCNT "
	lib$signal ( AMDS$_NOCONT )
   
                                                     
    
     * Increment alloc count... Then modify block size to be on a longwo
     * boundary for "congruity
     
    acount+
    if ( size < MIN_BLOCK_SIZE
	size = MIN_BLOCK_SIZ
    el
	size = (size + 3) & 0xFFFFFFF

    
     * Check to see if we can take this block from one of our look-aside lis
     * first... If not, then we'll bite the bullet and do the lib$get_
    
     * BTW: _REMQUE returns 0,1 when entry removed, and 2 when queue was emp
     
    idx = AMEM$K_SML_
    got_block = 2;	/* To fake the QUEUE_EMPTY 
    while ( idx < AMEM$K_INVALID ) 
   
	if ( size <= mem_list[idx].mem_q_size

	    
	     * To ensure that if our REMQUE fails that we allocate a blo
	     * big enough to fit on the queue when we dealloc the blo
	     * set up the block_size parameter to be the queue size
	     
	    block_size = mem_list[idx].mem_q_siz

	    /
	     * Are we messing with this queue alread
	     
	    if ( !_BBSSI( 0, &mem_list[idx].mem_q_lock )
	   
		
		 * Do we have block?  If so then decrement our count of bloc
		 * in our queue and then use the block in questi
		 
		got_block = _REMQUE ( (int *)mem_list[idx].mem_q_fl, &memblk 
		if ( got_block != 2 ) mem_list[idx].mem_q_len-
		mem_list[idx].mem_q_lock = 
	   

	    
	     * Since I don't want to allocate a "smaller" block from one of t
	     * larger sized queues, we force loop exit by setting the idx t
	     * the max.  This way whether or not I have taken an element o
	     * I won't be abusing my memory rights.
	     
	    idx = AMEM$K_INVALID

	idx+
   

    
     * Now I either have taken a block off one of my lookaside lists, or
     * do not yet have a block... If I don't yet have a block, then I ne
     * to allocate one
     
    if ( got_block == 2 )	/* Empty queue or I didn't get one... 
   
	
	 * If we have block that doesn't fit into one of our lookaside lis
	 * sizes, then set the block_size value to the size we want, sinc
	 * block_size is only "set" when we have a block that would fit.
	 
	if ( block_size == -1 ) block_size = siz

	block_size += AMEM$K_LENGTH;	/* Add in our header 
	status = lib$get_vm ( &block_size, &memblk 
	if ( status != SS$_NORMAL

	    lib$signal ( status 
	    return ( NULL 

#if DEB
	lastallocaddr = (int)membl
#end
	memblk->AMEM$L_SIZE = block_size; 	/* Entire block size.. 
    }						/* Including header 
    el
	memblk = (int)memblk - AMEM$K_LENGTH;	       	/* Since we queued the fl/bl 

    
     * Fill in some information about the allocated bloc
     
    memblk->AMEM$W_MARKER = 0xFEE
    memblk->AMEM$B_TYPE = typ
    memblk->AMEM$B_SUBTYPE = subtyp

    
     * Get to the area we are going to use
     
    blk = (int)memblk + AMEM$K_LENGT

#if DEBUG_M
    memset ( blk, 0x47, size 
#end
    blk->STD$L_FLINK = &blk->STD$L_FLIN
    blk->STD$L_BLINK = &blk->STD$L_FLIN
    blk->STD$W_SIZE = siz
    blk->STD$B_TYPE = typ
    blk->STD$B_SUBTYPE = subtyp

    return ( blk 




extern void mem_free ( blk, va_alist
    STD *bl
    va_dcl		/* NB: no semicolon 

**
**  FUNCTIONAL DESCRIPTIO

**	This routine will call LIB$FREE_VM for most cases.  Although 
**	we come across a deallocation on one of our special blocks th
**	DECamds uses alot, we will INSQUE the block onto a lookaside li
**	so that the allocation will make less calls to lib$get_v
*
*
**	When the memory is really deleted it will look as follow
*
**	---------------
**	|<size  | DEAD |
**	--------------
**	|sbt|typ| size>|
**	--------------
*
**	When the memory is placed on a lookaside list it will look as follow
**	  
**	---------------
**	|<size  | DEAF |
**	--------------
**	|sbt|typ| size>|
**	--------------
*
**  FORMAL PARAMETERS:

**	blk 		Address of block to dealloca
**	size 		NOT USED, but there so that lots of code doesn't ch

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**


    va_list a
    int q_insert = 0;		/* Assume false 
    int statu
    int siz
    int id
    int free_siz
    int max_factor = 
    AMEM *memblk = (int)blk - AMEM$K_LENGT
          
    
     * Make some checks for validi
     
    if ( memblk->AMEM$W_MARKER == 0xDEAD || memblk->AMEM$W_MARKER == 0xDEAF
   
	printf ( "\n***AMDS$INFO already deleted this block size: %d type: %d
		  memblk->AMEM$L_SIZE, memblk->AMEM$B_TYPE 
	retur
   
    else if ( memblk->AMEM$W_MARKER != 0xFEED
   
	printf ("\n***AMDS$INFO, corruption possible size: %d, type: %d, st: %d
		  memblk->AMEM$L_SIZE, memblk->AMEM$B_TYP
		  memblk->AMEM$B_SUBTYPE 
	retur
   

    
     * Get the size from the memory block header.  This is to be the si
     * used if we need to do a free_vm call.  otherwise, the size of our da
     * packet on a lookaside list would be the "free_size" - header_size (s
     * the mem_alloc routine above
     
    free_size = memblk->AMEM$L_SIZ
    if ( free_size < MIN_BLOCK_SIZE
   
	printf ("\n***AMDS$INFO, corruption, free_size: %d < 0.. type: %d, st:%d"
		  memblk->AMEM$L_SIZE, memblk->AMEM$B_TYP
		  memblk->AMEM$B_SUBTYPE 
	return;	/* punt 
   
    size = free_size - AMEM$K_LENGT

    
     * If we have console block (i.e., not RMCP) and event collect i
     * being used, then we want to save more data on our lookaside lis
     * since we'll likely use more.
     
	if  (console_blockP != NULL 
	     console_blockP->CSDB$R_FLAGS_OVERLA
			     CSDB$R_FLAG_BIT
			     CSDB$V_EVENT_COLLECT 
	    max_factor = 
	  

#if DEBUG_M
    memset ( blk, 0x92, size 
#end

    
     * Count this dalloc.. and reset our queue header for insertion.
     
    dcount+
    blk->STD$L_FLINK = &blk->STD$L_FLIN
    blk->STD$L_BLINK = &blk->STD$L_FLIN

    
     * First let's try to place the block on a look aside list so that we c
     * reuse the memory without the overhead of a get_vm call
     
    memblk->AMEM$W_MARKER = 0xDEAF;	/* Indicates not used, but allocated 
    idx = AMEM$K_SML_
    while ( idx < AMEM$K_INVALID ) 
   
	/* 
	 * If the size of the block (from the allocation) is the same as t
	 * current mem_q size and inserting the block won't "overflow" th
	 * maximum allowed elems in the queue.. then insert the element a
	 * exit the lo
	 
	if ( size == mem_list[idx].mem_q_size 
	     mem_list[idx].mem_q_len < ( max_factor * mem_list[idx].mem_q_max)

	    
	     * Are we already messing w/ this queu
	     
	    if ( !_BBSSI( 0, &mem_list[idx].mem_q_lock )
	   
		
		 * NOTE: We are inserting the "BLOCK" header into the queu
		 * 		not including our "mem header...
		 
		_INSQUE ( blk, (int *)mem_list[idx].mem_q_bl 
		mem_list[idx].mem_q_len+
		mem_list[idx].mem_q_lock = 
		q_insert = 
	   

	    
	     * Since we've done the insert, set the idx to the m
	     
	    idx = AMEM$K_INVALID

	idx+
   

    
     * If we didn't insert the element, then really delete it.
     * remember, free_size is the size we had allocate
     */                                                        
    if ( !q_insert
   
	memblk->AMEM$W_MARKER = 0xDEA
	status = lib$free_vm ( &free_size, &memblk 
	
	 * The BADBLOADR seems to come at odd times.. just ignore it.
	 * it gets signalled for far too many reasons to determine th
	 * root cause.. most likely though it's freeing well over a pa
	 * of memory and choki
	 
	if ( status != SS$_NORMAL && status != LIB$_BADBLOADR
	    lib$signal ( status 
   



#if DEB

static int not_static ( int btype, int size

    int retval = 

    switch ( btype
   
	/* List of "STATIC" types 
	case TYP$K_ACTION
	case TYP$K_ACTITM
	case TYP$K_CDB
	case TYP$K_CFG
	case TYP$K_CSDB
	case TYP$K_CSTDB
	case TYP$K_CST_INT
	case TYP$K_CST_FLT
	case TYP$K_DIDB 
	case TYP$K_EVENT
	case TYP$K_INTW
	case TYP$K_IOP
	case TYP$K_LADB
	case TYP$K_NDB
	case TYP$K_OSDB
	case TYP$K_OSVB
	case TYP$K_PLB
	case TYP$K_POPM
	case TYP$K_SECBUF
	case TYP$K_STD
	case TYP$K_SYSOB
	case TYP$K_THRDEF
	case TYP$K_THRVAL
	case TYP$K_TIMR
	case TYP$K_WIN

	    retval = 
	    brea

   

    return ( retval 


static void mem_dump (

**
**  FUNCTIONAL DESCRIPTIO


**  FORMAL PARAMETERS:
**              

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**


    struct	FAB	fab;		/* File Access Block 
    struct	RAB	rab;		/* Record Access Block 
    int statu
    int id


    
     * Load up the FAB. 
     
    fab = cc$rms_fab;				/* Default values for RMS 
    fab.fab$l_dna = "AMDS$DUMP:.LOG";		/* File specification 
    fab.fab$b_dns = strlen("AMDS$DUMP:.LOG"
    fab.fab$l_fna = "MEM-DUMP.LOG";		/* File name 
    fab.fab$b_fns = strlen("MEM-DUMP.LOG")
    fab.fab$w_mrs = 80;				/* Maximum record size 
    fab.fab$b_shr = FAB$V_SHRGET;		/* Shared GET access 
    fab.fab$l_alq = 
    fab.fab$w_deq = 1;        
    fab.fab$l_fop = FAB$M_CBT | FAB$M_SUP | FAB$M_SQO | FAB$M_DF
    fab.fab$b_fac = FAB$V_PU
    fab.fab$b_rat = FAB$V_PRN;             
    fab.fab$b_rfm = FAB$C_VA

    
     * Attempt to open our fi
     */                                                  
    status = sys$create ( &fab 
    if ( status != RMS$_NORMAL && status != RMS$_FILEPURGED 
   
	lib$signal ( status 
	retur
   

    
     * If we can at least CREATE the file we will set up the RA
     
    rab = cc$rms_rab;				/* Default RAB information 
    rab.rab$l_fab = &fab;			/* Our FAB 
    rab.rab$l_rop = RAB$M_EOF | RAB$M_WBH | RAB$M_RLK
                           
    
     * Connect to the file using our R
     
    status = sys$connect ( &rab 
    if ( status == RMS$_NORMAL 
   
	AMEM *memblk = (AMEM *)console_blockP;	/* Our first MEM_ALLOC! 
	int done = 
	int *nextadd
	int staticsize = 
	int nonstaticsize = 

	while (!done)                  

	    if (_PROBER ( PSL$C_USER, 4, memblk ) == 0 
	   
		if ( memblk > lastallocaddr 
	
		    char line [2*80
		    sprintf ( line
			  "Done at: %x, static_size: %d, nonstatic_size: %d"
			  memblk, staticsize, nonstaticsize 
		    rab.rab$l_rbf = line
		    rab.rab$w_rsz = strlen(line);   
		    status = SYS$PUT (&rab
		    if (status != RMS$_NORMAL) lib$signal ( status 
		    done = 
	
	   
	    el
	   
		/* is this location our memory? 
		if ( memblk->AMEM$W_MARKER == 0xFEED
	
		    if ( not_static ( (int)memblk->AMEM$B_TYP
				     memblk->AMEM$L_SIZE )
		   
			char line [2*80
			sprintf ( line
			      "Block at: %x, type: %d, st: %d, size: %d
			      memblk, (int)memblk->AMEM$B_TYP
			      (int)memblk->AMEM$B_SUBTYPE, memblk->AMEM$L_SIZE
                                                                            
			rab.rab$l_rbf = line
			rab.rab$w_rsz = strlen(line);   
			status = SYS$PUT (&rab
			if (status != RMS$_NORMAL) lib$signal ( status 
			if ( memblk->AMEM$B_TYPE >= 0 
			     memblk->AMEM$B_TYPE <= TYP$K_ZQS
			    nonstaticsize += memblk->AMEM$L_SIZ
		   
		    el
		   
			if ( memblk->AMEM$B_TYPE >= 0 
			     memblk->AMEM$B_TYPE <= TYP$K_ZQS
			    staticsize += memblk->AMEM$L_SIZ
		   
	
	   
            nextaddr = (int *)membl
	    nextaddr+
	    memblk = (AMEM *)nextadd

   
    el
	lib$signal ( status 

    /*
     * Close the file.
     
    sys$close ( &fab 

#end



**
**  FUNCTIONAL DESCRIPTIO


**  FORMAL PARAMETERS:
**              

**  IMPLICIT INPUTS:    no

**  IMPLICIT OUTPUTS:   no

**  RETURNS:            non

**  SIDE EFFECTS:       no
**

