{$DOUBLE}
Program REC1;

{ Version  X1.0
  File:[DECNET]EX1.PAS
  Author: Jim Bostwick 

  Last Edit: 9-OCT-1989 14:56:32 

  History:
	 9-OCT-1989 14:00:33 - JMB - fix up IOSB Byte values (again)
		 3-AUG-1989 10:50:29 - JMB - Add $DOUBLE switch


This is an example Pascal-2 DECNET application. It is a 'receiver' - 
it wakes up in response to a remote connect request, and receives
traffic from the net. 

Note: For brevity, a good bit of obvious error-checking has been omitted. 
	In particular, DSW is not checked following most Exec calls. This
	is done ONLY to remove clutter in this example - production programs
	should always detect and deal with errors on any Exec calls!
}

{$nolist}
{[a+,b+,l-,k+,r+] Pasmat }
%INCLUDE 'PAS$EXT:GENERAL.TYP';
%INCLUDE 'PAS$EXT:DECNET.PKG';
%INCLUDE 'PAS$EXT:STRING.PKG';
%INCLUDE 'PAS$EXT:CLEF.EXT';
%INCLUDE 'PAS$EXT:SETF.EXT';
%INCLUDE 'PAS$EXT:STSE.EXT';
%INCLUDE 'PAS$EXT:ALUN.EXT';

{$list}


Const
	Nt_EFN = f3;	{ use to signal network activity. By convention,
			    flag number = net mailbox LUN. }
	Aux_EFN = f1;	{ use for misc. things }

{
  Any network task must assign two LUNs for network use. The first is 
  for network control messages, and is called the mailbox LUN. The 
  network data queue is associated with this lun. The second LUN is 
  for network traffic - the link between tasks. There may be more than 
  one net LUN in use at one time, and thus more than one link active. 
}
	Nt_Mbx_lun = 3; { LUN for net data queue. The data queue does
			    *NOT* used for traffic, but for control messages. 
			    Only one mailbox LUN per task - it handles any
			    number of links.  }
	Nt_Lun = 4;	{ LUN for net link. This one is used for network
			    traffic. }


VAR
    IOSB: IO_STATUS_BLOCK;
    RecISB: IO_STATUS_BLOCK;	{ Used exclusively for no-wait net read }
    CONB: Net_Connect_block;
    Buff: CH80;			{ buffer for received data }
    Nul_Msg: CH16;		{ empty send message }
    Quit: Boolean;

BEGIN

{
  Initialization: 
    1. Generate an empty message for the 'control' network calls. 
       Your application may use these to pass control information
       during link setup, shutdown, and in XMI (net interrupt) 
       messges. 
    2. Clear the net activity EFN for safety's sake. 
    3. Assign LUNs for mailbox, link(s). Be sure Pascal doesn't
    	try to use the same LUNs!
    4. Open the network - required before any network activity. 
    5. Set up notification of mailbox traffic. 
}
    Quit := False;    { hey! we only just got started...}
    Sclear(Nul_msg);  { We use this various places. }
{
  A no-wait QIO will be used to read network data, and the IOSB
  (RecISB) examined to see if the QIO has completed. However, the
  QIO won't be posted until a link has been accepted. So, until
  that happens, we'll fake out a pending IO, by initializing the
  ISB to zero. 
{
  RecISB.int[1] := 0;

{ 
  Assign LUNS for net queue and net link 
}
    Alun(Nt_Mbx_lun,'NS',0);
    Alun(Nt_lun,'NS',0);
    CLEF(Nt_Efn);	    { clear activity flag }
{
 Call NTOPN to get a network LUN and start DECNET.
 This is required first network call (other than possibly NTCONB)
 in any network task. 
}
    NtOpn(Nt_Mbx_lun,Aux_EFN,0,0,IOSB);
        IF IOSB.INT[1] = 1 THEN	
	    BEGIN
{
 Call NTSPA to set up net que notification.
 NTSPA sets up an internal (hidden) AST service routine which
 sets NT_EFN whenever new traffic appears in the net mailbox. 
}
	    NtSPA(Nt_Mbx_lun,Nt_EFN,IOSB);
{
  Traffic on the network mailbox (aka network data queue)
  will fire the AST set up by NTSPA, and the service routine will set 
  NT_EFN. 

  We now enter the 'main loop', which waits for something to set Nt_EFN, 
  then processes it.  Always check the mailbox first, as high-priority
  messages go there. Also, network abort notification comes through the 
  mailbox. 
}
	    Repeat
		STSE(Nt_EFN);	{ wait for something interesting }
		CLEF(Nt_EFN);	{ clear now to minimize possibility of 
				  missing something }
{
  Call NTGND repeatedly to flush network mailbox queue.
}
		Repeat
		    NTGND(Nt_Mbx_lun,Aux_Efn,Conb,IOSB);
{ 
  Here follows a crude routine to process various network events. 
  Use of the byte_status_block simplifies this, as DECNET status
  is highly byte oriented. 
}
		    IF (IOSB.Byt[1] = NT_IE_NDA) 
			THEN Writeln('The queue is empty.')
			ELSE CASE IOSB.Byt[2] of
			    Nt_NT_CON :	{ Connect }
				BEGIN
				    NTACC(Nt_Lun,Aux_EFN,Conb,Nul_msg,IOSB);
				    NTDCB(Conb);
{
 Call NtREC to post no-wait receive. 
 We must wait to do this until a link has been set up (by NTACC). 
}
				    NtRec(Nt_lun,Nt_EFN,loophole(address,
							ref(buff)),80,RecISB);

{ 
  Accept network connection. You may wish to decide (from things in 
  the net_connect_block) to reject the connect. We use Aux_EFN here, 
  as we don't want the NTACC to signal new network activity. 
  Also, we call a normally debug-only routine to dump the contents of 
  the new connect block. 
}
				END; { connect }
			    Nt_NT_INT :	{ Interrupt message }
				Writeln('Interrupt message, length = ', IOSB.Byt[3] 
				    ,' LUN = ', IOSB.Byt[4],'.');
{ 
  It would be smart here to go into Conb and look at the interrupt message...
}
			    Nt_NT_DSC :	{ User synchronous disconnect }
				BEGIN
				    Writeln('Network Disconnect.');
				    Quit := True
				END;
{ 
  Network disconnect is the normal way out. It allows the queues to run
  down before breaking the connection. 
}
			    Nt_NT_ABT :	{ User abort }
				BEGIN
				    Writeln('Network User Abort.');
				    Quit := True
				END;
			    Nt_NT_ABO :	{ Network abort }
				BEGIN
				    WRiteln('Network System Abort.');
				    Quit := True
				END;
{ 
  Network and User aborts should be treated the same - get the heck
  out off the net ASAP! DECNET flushes the mailbox and link queues
  for either of these calls. You can't refuse to abort - by the time
  you're told, it's already happened. 
}
			    Otherwise Writeln('do something else');	
			    END {Case}
		Until (QUIT OR (IOSB.Byt[1] = NT_IE_NDA));
{ 
  Having dealt with the mailbox, check each link for a completed net
  receive. If we've gotten an abort or disconnect, skip this section
  and exit. 
}
		Writeln('RecISB.int[1]=',RecISB.int[1]:-6);
		IF NOT(Quit) THEN
		    IF (RecISB.INT[1] <> 0) THEN
			BEGIN
			    IF (RecISB.INT[1] = 1) THEN
				BEGIN
{ 
  Hot damn! We got something. Process it, then post a new
  NtRec. 
}
			    	    Swrite(output,buff);
				    Writeln;
				    NtRec(Nt_lun,Nt_EFN,loophole(address,
					ref(buff)),80,RecISB)
				END
				ELSE { RecISB[1] < 0 --> error }
				    Quit := True
{
  This is a hack - quit on any network receive error. It might be wise
  to retry, analyze the error, or other intelligent programmer stuff. 
}
			END;
	    UNTIL (QUIT);
{
  We've done our thing. Be nice, and close down the network before exiting. 
  If you don't, the Exec will close it for you, but will issue a net abort
  to your partner - which he'll probably assume means you died rather than
  quit. 
}
	NtCLS(Nt_mbx_lun,Nt_efn,IOSB);
	end
end.


{ 	FURTHER NOTES

 As written, the example will process one mailbox message, then one link
message, then repeat. It would be better to loop through the NtGND call until
the mailbox is empty, then go process one or more link reads (NtRec). 
 Be careful that your code is immune to missed completion events. The example
is somewhat immune, but does allow a timing window: If two mailbox items 
arrive before the task wakes up and clears the event flag, one could be left
in the mailbox. Better pseudo-code is:
    Repeat
	wait for Nt_Efn
	Clear Nt_EFN
	Repeat
	    NtGND (read mailbox)
	    process
	Until Mailbox is empty
	IF Network Read (NtRec) completed
	    then 
		process input
		post new read
		end
    Until done

This is proof against missed events, because the mailbox is flushed fully.

}
