{$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. }