Trailing-Edge
-
PDP-10 Archives
-
decuslib10-08
-
43,50512/netio.b36
There are no other files named netio.b36 in the archive.
MODULE NETIO=
!Tops-10 Network device I/O for NETSPL
BEGIN
!
! Table of Contents
!
FORWARD ROUTINE
NDBINI, !Initialize an NDB
LINK, !Open a logical link
NODENN, !Get node number or name
XINPUT, !Read in a buffer from the network
XOUTPUT, !Write out...
!XREAD, !Read a string from net
XWRITE, !Write a string to net
XBINA, !Get byte, write thru pointer & increment pointer
XBIN, !Get a byte & return it
XBOUTA, !Put byte & increment source byte pointer
XBOUT, !Put byte out
XMTMSG, !Output a DAP message
ENTER, !Enable output to a TSK
TSK; !Do a TSK. UUO for 7.00 or later
!
! Conditional compilation
!
COMPILETIME FTPSI=0; !On to trust software interrupt system
COMPILETIME FTTRACE=-1; !On to allow DAP message tracing
!
! Require & Library files
!
REQUIRE 'INTR.REQ';
LIBRARY 'NODTBL';
!
! Version
!
THIS_IS [NETI]
VERSION [2] EDIT [22] DATE [14,DEC,79]
%(
Revision History
[22] Allow control of # of data requests
[21] Allow # of input & output TSK buffers to be set separately
Since 7.00 can use lots of input buffers, but only 1 output buffer.
[20] Use of DC_DAR & DC_DAT was backwards. fixed it.
[17] Use new stuff for connects also
[16] Make this work on 7.00 monitor, use TSK. UUO in new routine TSK
[15] Check for TSK available before attempting to open it
[14] Make LINK routine impervious to spurious interrupts for active tasks
[13] Set # of arguments in interrupt block correctly
[12] More cleanup of bound refs.
[11] Make LINK routine defend against spurious interrupts
for passive tasks.
)%
!++
! Facility: NETSPL,RMCOPY,NODTBL
!
!Abstract:
! Subroutines to interface to the network device 'TSK' on TOPS-10
! These routines do non-blocking I/O to the network. They interact
! with the software interrupt system via the SIGNALling mechanism
! used in NETSPL (modules INT & INTSIG, INTR.REQ, INTR.BLI)
! They also do NCL segmentation REQUIRING MONITOR MODS TO WORK.
! If the link word in a buffer header AND 200000,,0 NEQ 0, then
! you haven't gotten to the end of the record yet.
! See macros INOEOR & ONOEOR. These routines set timers according
! to NDB$CONTO & NDB$REPTO to time out if a timely response is
! not forthcoming. If the values are 0, the feature is disabled.
!
!Author: Andrew Nourse
!
!
! Literals
!
LITERAL
DEFAULT_INNETB=1, !1 network buffer for input under 6.03
DEFAULT_ONNETB=1, !1 network buffer for output
DEFAULT_CONTO=360, !Default timeout for connect = 2 minutes
DEFAULT_REPTO=180; !Default timeout for message = 1 minute
BUILTIN MACHOP;
LITERAL DEV_AVAIL=%O'40000000',
DEV_OPENED=%O'200000';
LITERAL TSK_=%O'177', !TSK. UUO is this CALLI
_TKFRS=1, !Function code for TSK read status
_TKFEP=2, !Function code for TSK enter Passive state
_TKFEA=3, !Function code for TSK enter Active state
_TKFEI=4, !Function code for TSK enter Idle state
_TKFWT=5, !Function code for TSK wait for connect
_TKFOT=6, !Function code for TSK output
_TKFIN=7, !Function code for TSK input
DC_DAT=1, !NCL DAP code for data w/o end of record
DC_DAR=2, !NCL DAP code for data with end of record
TS_UDW=%O'14', !Error code for I/O-type error
NPDLEN=16; !Plenty of room in NPD
BIND LOCAL_PID=UPLIT(%ASCIZ 'FTS[1,2]'),
LOCAL_PID_LEN=8;
!
! Macros
!
MACRO TSK_INPUT(NB)=
BEGIN
LOCAL TASK_BLOCK: VECTOR[3];
TASK_BLOCK[0]=_TKFIN;
TSK(NB,3,TASK_BLOCK);
.TASK_BLOCK[2] !Return (NCL) DAP code
END %,
TSK_OUTPUT(NB,DAP_CODE)=
BEGIN
LOCAL TASK_BLOCK: VECTOR[3];
TASK_BLOCK[0]=_TKFOT;
TASK_BLOCK[2]=DAP_CODE;
TSK(NB,3,TASK_BLOCK);
END %;
MACRO DEVCHR(REG)=MACHOP(%O'47',REG,4) %;
MACRO IOERRCODE(X)=
BEGIN
IF (.X XOR ENDFILE) EQL 0
THEN WARNING(ENDFILE,.FILBLK)
ELSE ERROR(IOERROR,.FILBLK,.X)
END%;
MACRO ERR_INTERPRET=
BEGIN
%IF FTSIG
%THEN
IF .FF EQL 0
THEN BEGIN
LOCAL IB: REF INT_BLOCK;
%IF NOT FTPSI %THEN
EXTERNAL ROUTINE
UDT,
TSIGNL,
TSICAN;
LOCAL RETRYIT: INT_BLOCK;
LOCAL RTR; !Handle for timer
CLEARV(RETRYIT);!Empty block for wakeup only
RTR=TSIGNL(RETRYIT,UDT()+.IORETINTERVAL);
%FI
IF (IB=.FILBLK[FILE$IB]) EQL 0
THEN CRASH('Interrupt block missing');
WAIT(.IB); !Wait for 'done' interrupt
%IF NOT FTPSI %THEN TSICAN(.RTR); %FI
END
ELSE IOERRCODE(FF)
%ELSE IOERRCODE(FF)
%FI
END%;
MACRO NPD=BLOCK[NPDLEN] FIELD(NPD_FIELDS) %;
!
! Field Definitions
!
FIELD NPD_FIELDS=
SET
NPD$NODE=[0,0,36,0], !Node # (not name!) or -1 for any
NPD$PIDLEN=[1,0,36,0], !# of chars in PID
NPD$PID=[2,0,0,0] !PID starts here
TES;
!
! Global Data
!
GLOBAL
RNAME: INITIAL(%SIXBIT'FTSFAL'),
INNETB: INITIAL(DEFAULT_INNETB), !# of input TSK buffers
ONNETB: INITIAL(DEFAULT_ONNETB), !# of output TSK buffers
DCONTO: INITIAL(DEFAULT_CONTO),
DREPTO: INITIAL(DEFAULT_REPTO),
MAXDRQ; !Max for INNETB (Set at startup)
!
! Externals
!
EXTERNAL ROUTINE
FILOP, !File Open/Close...
INTINI, !Initialize an interrupt block
UDX, !Get universal device index
WRSIXA, !convert SIXBIT to ASCII
WRPPNA, !convert PPN to ASCII
MOVEAZ, !Move asciz string
CHKCON, !Check for connect confirmed
ALLOC, !Get some core
LKNODE, !Find a node in NODTBL
ZERO; !Clear a block of core
%IF NOT FTPSI %THEN
EXTERNAL ROUTINE
UDT, !Universal date & time
TSIGNL, !Set alarm clock
TSICAN; !Turn off alarm clock
%FI
EXTERNAL RUN: REF PROCESS_BLOCK;
EXTERNAL IORETINTERVAL; !How often to check for I/O completion
BUILTIN MACHSKIP;
!
! Macros
!
!
! Routines
!
GLOBAL ROUTINE NDBINI(FILBLK)=
!This sets up a file block for the first time it gets used
!It only sets up pointers, not filespecs.
BEGIN
EXTERNAL ROUTINE
FFIOCH;
MAP FILBLK: REF NDB;
!First Zero out everything
ZERO(.FILBLK,.FILBLK+NDB_LEN-1);
!Now fill in the required pointers in the FILOP. block
FILBLK[FILE$COUNT]=%FIELDEXPAND(FILE$ALLOC,0)-%FIELDEXPAND(FILE$COUNT,0);
!# OF WORDS IN LOOKUP/ENTER BLOCK
FILBLK[FILE$LOOKUP]=FILBLK[FILE$COUNT]; !Set up pointer to lookup block
FILBLK[FILE$PATH_LEN]=SFDMAX+3;
FILBLK[FILE$PATH_BLOCK]=FILBLK[FILE$PATH_FUN];
FILBLK[FILE$I_BRH]=FILBLK[FILE$I_CBUFF]; !Input buffer ring header pointer
FILBLK[FILE$O_BRH]=FILBLK[FILE$O_CBUFF]; !Output ...
FILBLK[FILE$I_NBUFF]=.INNETB; !Set # of buffers
FILBLK[FILE$O_NBUFF]=.ONNETB; ! " "
FILBLK[FILE$DEVICE]=%SIXBIT 'TSK ';
FILBLK[FILE$NAME]=.RNAME; !Task name
%IF %VARIANT AND 8
%THEN
FILBLK[FILE$MODE]=UU_NBE+UU_AIO+_IOIMG+UU_DMR;
!Non-blocking,non-blocking enter, byte, disable message reassembly
%ELSE
FILBLK[FILE$MODE]=UU_NBE+UU_AIO+_IOBYT+UU_DMR;
!Non-blocking,non-blocking enter, byte, disable message reassembly
%FI
FILBLK[FILE$CHANNEL]=FFIOCH(.FILBLK); !Get a free I/O channel
FILBLK[FILE$PF_NODE_A]=1; !So everyone knows it's an NDB
!Set default timeout values (unless debugging)
IF (.DREPTO NEQ 0) THEN FILBLK[NDB$REPTO]=.DREPTO;
IF (.DCONTO NEQ 0) THEN FILBLK[NDB$CONTO]=.DCONTO;
RUN[P$NDB]=.FILBLK; !This is the NDB for this process
END;
GLOBAL ROUTINE LINK(NB)=
!Open a logical link
!The link will be open, with interrupts set up, when the routine returns
!Argument:
!NB: Node data block
! This is a local task if NDB$NODEID is 0, otherwise it is remote
BEGIN
MAP NB: REF NDB;
LOCAL LOCNPD: NPD, !Our NPD (7.00)
REMNPD: NPD; !Other system's NPD (7.00)
IF .NB[NDB$NODEID] NEQ 0 THEN
BEGIN !This is a remote task
LOCAL NODENAME; !Name of node to connect to
LOCAL NNM; !Node number
LOCAL NODE: REF NODTBL_ENTRY; !The entry in NODTBL
NODENAME=.NB[NDB$NODEID]; !Use the destination by default
NODE=LKNODE(.NODENAME); !Try to look up in NODTBL
IF .NODE[NOD$CNAME] NEQ 0
THEN NB[FILE$NAME]=.NODE[NOD$CNAME]; !Use taskname if any
NB[FILE$LPPN]=(IF .NODE[NOD$CPPN] NEQ 0
THEN .NODE[NOD$CPPN] !Use ppn given if any
ELSE XWD(1,2));
IF .NODE[NOD$ROUTE] NEQ 0
THEN NODENAME=.NODE[NOD$ROUTE]; !Use NOD$ROUTE if any
NNM=NODENN(.NODENAME); !Get the node number for the node
IF .NNM EQL 0 THEN ERROR(FILUNN,.NB); !He ain't there
IF700 !Use new stuff for 7.00 or later
THEN BEGIN
LOCAL PT;
LOCNPD[NPD$NODE]=0; !Ignored anyway
LOCNPD[NPD$PIDLEN]=LOCAL_PID_LEN;
MOVEAZ(%REF(CH$PTR(LOCAL_PID)),%REF(CH$PTR(LOCNPD[NPD$PID])));
REMNPD[NPD$NODE]=.NNM; !Store the node number
PT=CH$PTR(REMNPD[NPD$PID]); !Start the name here
WRSIXA(.NB[FILE$NAME],PT); !Store the name
WRPPNA(.NB[FILE$LPPN],PT); !And the PPN
REMNPD[NPD$PIDLEN]=
CH$DIFF(.PT,CH$PTR(REMNPD[NPD$PID])); !# Of characters in PID
END
ELSE BEGIN !This is for 6.03/6.03A
(NB[FILE$DEVICE])<6,6>=(.NNM AND 7)+%O'20';
(NB[FILE$DEVICE])<12,6>=((.NNM ^ -3) AND 7)+%O'20';
!Stuff the node number into the device name
END;
NB[NDB$MASTER]=1; !We are the active task
END
ELSE BEGIN
NB[NDB$MASTER]=0; !We are a passive task
END;
!Rejoin common code
IF .NB[FILE$IB] EQL 0
THEN INTINI(NB[FILE$IB]=ALLOC(INT_LEN));
!Create an interrupt block if we don't have one
!Open the link & try to connect
BEGIN
BIND IB=.NB[FILE$IB]: INT_BLOCK;
REGISTER R;
DO BEGIN
LOCAL T; !Handle for timer if we set it
LOCAL WFLINK: INT_BLOCK; !To wake up periodically & check
CLEARV(WFLINK); !for an available link
WFLINK[INT$PROCESS]=.RUN; !Wake us, that's all
R=.NB[FILE$DEVICE]; !See if there are any links to there
DEVCHR(R);
IF ((.R AND DEV_OPENED) NEQ 0) OR ((.R AND DEV_AVAIL) EQL 0)
THEN BEGIN
IF .NB[NDB$MASTER]
THEN ERROR(LNKNAV,NB[FILE$START]) !none, try later
ELSE BEGIN !Wait for one
T=TSIGNL(WFLINK,UDT()+.DCONTO);
WAIT(WFLINK);
TSICAN(.T);
END
END
ELSE EXITLOOP;
END WHILE 1;
IB[INT$PROCESS]=.RUN; !These interrupts belong to us
RUN[P$NDB]=.NB; !This is our NDB
OPEN_R(.NB);
IB[INT$WHAT]=.NB[FILE$CHANNEL];
WAKESET(IB,IN_DONE,OUT_DONE,ONLINE);
ERRSET(IB,IN_ERROR,OUT_ERROR,OFFLINE);
IB[INT$SIGNAL_ARGS]=4; !Set up arguments for interrupts
IB[INT$SEVERITY]=SS$_ERROR;
IB[INT$STSCODE]=IOINT;
IB[INT$FILBLK]=.NB;
INTERRUPTS(ADD,IB);
BEGIN
%IF NOT FTPSI %THEN
LOCAL LINKRETRY: INT_BLOCK,
LR; !Handle for timer
CLEARV(LINKRETRY);
%FI
IF .NB[NDB$MASTER] NEQ 0
!Do ENTER, wait for interrupt, then check for good connection
THEN BEGIN !Active task, ENTER first
IF700
THEN BEGIN
LOCAL TSKBLK: VECTOR[4];
TSKBLK[0]=_TKFEI; !Must be idle first
TSK(NB[FILE$START],2,TSKBLK); !...
TSKBLK[0]=_TKFEA; !Enter active state
TSKBLK[2]=LOCNPD; !Local NPD
TSKBLK[3]=REMNPD; !Remote NPD
(TSKBLK[2])<LH>=(TSKBLK[3])<LH>=NPDLEN;
!Length of NPD's
TSK(NB[FILE$START],4,TSKBLK); !Do it
END !7.00 and later
ELSE BEGIN !6.03/6.03A
ENTER(.NB);
END; !6.03/6.03A
WHILE CHKCON(NB[FILE$START]) EQL 0
DO BEGIN
%IF NOT FTPSI %THEN !Set timer
LR=TSIGNL(LINKRETRY,UDT()+.IORETINTERVAL);
%FI
WAIT(IB);
%IF NOT FTPSI %THEN TSICAN(.LR); %FI
END
END
ELSE BEGIN !Passive Task, wait for interrupt then ENTER
DO BEGIN
%IF NOT FTPSI %THEN !Set timer
LR=TSIGNL(LINKRETRY,UDT()+.IORETINTERVAL);
%FI
WAIT(IB);
%IF NOT FTPSI %THEN TSICAN(.LR); %FI
END WHILE CHKCON(NB[FILE$START]) EQL 0;
!Make sure we really got a connect
IF700 THEN () ELSE ENTER(.NB);
END;
END;
END;
END; !LINK
GLOBAL ROUTINE NODENN(NN)=
!Routine to return a node number from a node name or visa-versa
BEGIN
REGISTER T; !Register for UUO
LOCAL NODE_UUO_BLK: VECTOR[2];
T=NODE_UUO_BLK; !Addr
T<18,18>=2; !Function code
NODE_UUO_BLK[0]=2; !Length of argument list (includes this word)
NODE_UUO_BLK[1]=.NN; !Sixbit NODEID or node number
IF CALLI(T,%O'157') !NODE. UUO
THEN RETURN .T !Return number or name
ELSE RETURN 0; !Node isn't there
END; !NODENN
GLOBAL ROUTINE XINPUT(FILBLK)=
!ROUTINE TO INPUT A BUFFER.
!TAKES FILE BLOCK AS ARGUMENT
!RETURN WIN
BEGIN
MAP FILBLK: REF NDB;
LOCAL TTO,
INTMO: INT_BLOCK;
EXTERNAL ROUTINE
INPUT;
ROUTINE XINHANDLE(SIGNAL_ARGS,MECH_ARGS,ENABLE_ARGS)=
BEGIN
MAP SIGNAL_ARGS: REF VECTOR,
MECH_ARGS: REF VECTOR,
ENABLE_ARGS: REF VECTOR;
BIND NB=.ENABLE_ARGS[1]: NDB;
SELECT .$CODE OF SET
[TIMOUT]: BEGIN
IF .NB[FILE$I_COUNT] GEQ 0
THEN BEGIN !Buffer has been sent
EXTERNAL RUN: REF PROCESS_BLOCK;
RUN[P$WAIT]=0;
RETURN SS$_CONTINUE;
END;
END;
[ALWAYS]: RETURN SS$_RESIGNAL;
TES
END;
ESTABLISH (XINHANDLE,.FILBLK);
IF (TTO=.FILBLK[NDB$REPTO]) NEQ 0 THEN
BEGIN
CLEARV(INTMO);
INTMO[INT$SIGNAL_ARGS]=2; !Length of argument list
INTMO[INT$STSCODE]=TIMOUT;
INTMO[INT$SEVERITY]=SS$_ERROR;
INTMO[INT$STATUS]=INERROR; !Remember it was input
TTO=TSIGNL(INTMO,(UDT()+.TTO));
END
ELSE TTO=-1; !No limit
IF700
THEN BEGIN
LOCAL NOEOR;
INOEOR(FILBLK)=0; !Clear old style bit
NOEOR=TSK_INPUT(FILBLK[FILE$START]);
!Do TSK. UUO with appropriate function code
INOEOR(FILBLK)=
(IF .NOEOR EQL DC_DAT THEN 1 ELSE 0); !Set the bit from the NCL DAP
END
ELSE INPUT(FILBLK[FILE$START]);
IF .TTO NEQ -1
THEN BEGIN
TSICAN(.TTO); !Cancel timeout request if any
END;
IF NOT .FILBLK[NDB$MFLAG_LEN]
THEN FILBLK[NDB$MLENGTH]=.FILBLK[NDB$NSPMLENGTH]+.INOEOR(FILBLK);
!If we don't know how long the message is supposed to be,
!it must be the whole packet, at least. If no end of record, it
!must be at least 1 character longer than that.
%IF FTTRACE %THEN
IFMSG(TRACE,(EXTERNAL ROUTINE TIBUF; TIBUF(FILBLK[FILE$START])));
%FI; !Type the message, if requested
WIN
END;
GLOBAL ROUTINE XOUTPUT(FILBLK)=
!ROUTINE TO OUTPUT A BUFFER.
!TAKES FILE BLOCK AS ARGUMENT
!RETURN WIN
BEGIN
MAP FILBLK: REF NDB;
LOCAL T, !Store value from OUTPUT
OUTTMO: INT_BLOCK, !Interrupt block for timeout request
TTO; !When to time out
EXTERNAL ROUTINE
TSIGNL,
UDT,
TSICAN,
OUTPUT;
ROUTINE XOUTHANDLE(SIGNAL_ARGS,MECH_ARGS,ENABLE_ARGS)=
BEGIN
MAP SIGNAL_ARGS: REF VECTOR,
MECH_ARGS: REF VECTOR,
ENABLE_ARGS: REF VECTOR;
BIND NB=.ENABLE_ARGS[1]: NDB;
SELECT .$CODE OF SET
[TIMOUT]: BEGIN
IF ..NB[FILE$O_CBUFF] GEQ 0
THEN BEGIN !Buffer has been sent
EXTERNAL RUN: REF PROCESS_BLOCK;
RUN[P$WAIT]=0;
RETURN SS$_CONTINUE;
END;
END;
[ALWAYS]: RETURN SS$_RESIGNAL;
TES
END;
ESTABLISH (XOUTHANDLE,.FILBLK);
%IF FTTRACE %THEN
IFMSG(TRACE,(EXTERNAL ROUTINE TOBUF; TOBUF(FILBLK[FILE$START])));
%FI; !Type the message, if requested
IF (TTO=.FILBLK[NDB$REPTO]) NEQ 0 THEN
BEGIN
CLEARV(OUTTMO);
OUTTMO[INT$SIGNAL_ARGS]=2; !length of argument list
OUTTMO[INT$STSCODE]=TIMOUT;
OUTTMO[INT$SEVERITY]=SS$_ERROR; !Bomb out transfer if not caught
OUTTMO[INT$STATUS]=OUTERROR;
TTO=TSIGNL(OUTTMO,(UDT()+.TTO));
END
ELSE TTO=-1;
IF700
THEN BEGIN
LOCAL NOEOR;
NOEOR=.ONOEOR(FILBLK);
ONOEOR(FILBLK)=0; !Clear old style bit
T=TSK_OUTPUT(FILBLK[FILE$START],(IF .NOEOR THEN DC_DAT ELSE DC_DAR));
!Do TSK. UUO with appropriate function code
END
ELSE T=OUTPUT(FILBLK[FILE$START]);
%IF %DECLARED(NDB$MAXNSP)
%THEN
IF .FILBLK[NDB$MAXNSP] NEQ 0
THEN FILBLK[FILE$O_COUNT]=.FILBLK[NDB$MAXNSP];
%FI
IF .TTO NEQ -1
THEN BEGIN
TSICAN(.TTO); !Cancel timeout request
END;
.T
END;
GLOBAL ROUTINE XIN_NOWAIT(FILBLK)=
!Same as XINPUT but does not ever call WAIT
BEGIN
MAP FILBLK: REF NDB;
BIND NOWAIT=.FILBLK[FILE$NOWAIT]; !Save state of bit
LOCAL V;
FILBLK[FILE$NOWAIT]=1;
V=XINPUT(.FILBLK);
FILBLK[FILE$NOWAIT]=NOWAIT;
.V
END; !XIN_NOWAIT
GLOBAL ROUTINE XOUT_NOWAIT(FILBLK)=
!Same as XOUTPUT but does not ever call WAIT
BEGIN
MAP FILBLK: REF NDB;
BIND NOWAIT=.FILBLK[FILE$NOWAIT];
LOCAL V;
FILBLK[FILE$NOWAIT]=1;
V=XOUTPUT(.FILBLK);
FILBLK[FILE$NOWAIT]=NOWAIT;
.V
END; !XOUT_NOWAIT
GLOBAL ROUTINE XREAD(FILBLK,DEST,MAXCHARS,TERMINATOR)=
!ROUTINE TO READ A STRING FROM A FILE
!PARAMETERS:
!FILBLK: FILE BLOCK (SOURCE)
!DEST: ADDR OF DESTINATION BYTE POINTER
!MAXCHARS: MAXIMUM # OF CHARACTERS TO INPUT;
!TERMINATOR: TERMINATE INPUT IF THIS CHARACTER IS FOUND
!RETURNS WIN if count exhausted, TERMINATOR otherwise
BEGIN
LOCAL C;
DECR MAXC FROM .MAXCHARS TO 1 DO BEGIN
C=XBINA(.FILBLK,.DEST);
IF CH$RCHAR(..DEST) EQL .TERMINATOR THEN RETURN .TERMINATOR;
END;
RETURN WIN;
END;
GLOBAL ROUTINE XWRITE(FILBLK,ADDR,MAXCHARS,TERMINATOR)=
!ROUTINE TO WRITE A STRING TO A FILE
!Parameters:
!FILBLK: FILE BLOCK (Destination)
!ADDR: ADDR OF SOURCE BYTE POINTER
!MAXCHARS: MAXIMUM # OF CHARACTERS TO OUTPUT;
!TERMINATOR: TERMINATE OUTPUT IF THIS CHARACTER IS FOUND
!Returns WIN if count exhausted, TERMINATOR otherwise
BEGIN
LOCAL C;
DECR MAXC FROM .MAXCHARS TO 1 DO BEGIN
C=XBOUTA(.FILBLK,.ADDR);
IF CH$RCHAR(..ADDR) EQL .TERMINATOR THEN RETURN .TERMINATOR;
END;
END;
GLOBAL ROUTINE XBINA(FILBLK,DEST)=
!READ ONE BYTE FROM A FILE and increment destination byte pointer
!FILBLK: FILE BLOCK
!DEST: Addr of DESTINATION BYTE POINTER
!RETURNS WIN
BEGIN
LOCAL R;
MAP FILBLK: REF NDB;
UNTIL (FILBLK[NDB$NSPMLENGTH]=.FILBLK[NDB$NSPMLENGTH]-1) GEQ 0 DO
BEGIN
IF .INOEOR(FILBLK) EQL 0 THEN ERROR(DAPEOM);
R=XINPUT(.FILBLK);
END;
CH$WCHAR_A(CH$RCHAR_A(FILBLK[FILE$I_PTR]),.DEST);
FILBLK[NDB$MLENGTH]=.FILBLK[NDB$MLENGTH]-1; !Decr the DAP length too
WIN !SUCCESSFUL RETURN VALUE
END;
GLOBAL ROUTINE XBIN(FILBLK)=
!READ ONE BYTE FROM A FILE
!FILBLK: FILE BLOCK
!RETURNS BYTE READ
BEGIN
LOCAL R;
MAP FILBLK: REF NDB;
UNTIL (FILBLK[NDB$NSPMLENGTH]=.FILBLK[NDB$NSPMLENGTH]-1) GEQ 0 DO
BEGIN
IF .INOEOR(FILBLK) EQL 0 THEN ERROR(DAPEOM);
R=XINPUT(.FILBLK);
END;
FILBLK[NDB$MLENGTH]=.FILBLK[NDB$MLENGTH]-1; !Decr the DAP length too
CH$RCHAR_A(FILBLK[FILE$I_PTR])
END;
GLOBAL ROUTINE XBOUTA(FILBLK,SOURCE)=
!WRITE ONE BYTE TO A FILE and increment source pointer
!FILBLK: FILE BLOCK
!SOURCE: Addr of SOURCE BYTE POINTER
!Returns WIN
BEGIN
MAP FILBLK: REF NDB;
IF (FILBLK[FILE$O_COUNT]=.FILBLK[FILE$O_COUNT]-1) LSS 0 THEN
BEGIN
ONOEOR(FILBLK)=1; !Indicate not yet end of message
XOUTPUT(.FILBLK);
END;
CH$WCHAR_A(CH$RCHAR_A(.SOURCE),FILBLK[FILE$O_PTR]);
WIN !SUCCESSFUL RETURN VALUE
END;
GLOBAL ROUTINE XBOUT(FILBLK,SOURCE)=
!WRITE ONE BYTE TO A FILE
!FILBLK: FILE BLOCK
!SOURCE: Byte to write
!Returns WIN
BEGIN
MAP FILBLK: REF NDB;
UNTIL (FILBLK[FILE$O_COUNT]=.FILBLK[FILE$O_COUNT]-1) GEQ 0 DO
BEGIN
ONOEOR(FILBLK)=1; !Indicate not yet end of message
XOUTPUT(.FILBLK);
END;
CH$WCHAR_A(.SOURCE,FILBLK[FILE$O_PTR]);
WIN !SUCCESSFUL RETURN VALUE
END;
GLOBAL ROUTINE XMTMSG(NB,ADDR,LEN)=
!Routine to send a message
!Arguments:
!NB: NDB for node
!ADDR: address of message
!LEN: length of message
BEGIN
MAP NB: REF NDB;
LOCAL PTR; !Temp byte pointer
PTR=CH$PTR(.ADDR,0,8); !Make pointer point to message
DECR L FROM .LEN TO 1 DO XBOUTA(.NB,PTR);
END;
GLOBAL ROUTINE ENTER(NB)=
!Routine to do an 'ENTER' to a file block
!FILOP cannot do this correctly for a TSK
!ARGUMENT
!NB: NDB
BEGIN
MAP NB: REF NDB;
REGISTER FF; !AC To build instruction in
MAP FF: INSTRUCTION;
FF=0;
FF[INSTR$OPCODE]=%O'77'; !Opcode for ENTER UUO
FF[INSTR$AC]=.NB[FILE$CHANNEL]; !Channel # in AC field
FF[INSTR$ADDRESS]=.NB[FILE$LOOKUP]; !Address is addr of lookup block
IF MACHSKIP(%O'256',0,FF) !XCT an ENTER UUO
THEN RETURN WIN !It worked!
ELSE ERROR(FILERR+.NB[FILE$ADATE])
!Of all the wierd places to return an error code!!
END; !ENTER
GLOBAL ROUTINE TSK(FILBLK,LEN,TSKBLK)=
!Do A TSK. UUO for 7.00 or later
! FILBLK= NDB (task file block) for link
! LEN= Length of argument block
! TSKBLK= Address of argument block
! The NCL DAP code from an input function (_TKFIN) will be returned
!
! Abstract: BLISS-36 interface to the TOPS-10 7.00 TSK. UUO.
!
%( TSK. UUO calling sequence:
MOVE AC,[LEN,,BLK]
TSK. AC, ;TSK. = CALLI 177
(ERROR return)
(WIN return)
.
.
.
BLK[0]= Function code: One of the following
1: Read status
2: Enter Passive state
3: Enter Active State
4: Enter Idle State
5: Wait for Connect Confirm
6: Output (Send) 1 packet
7: Input (receive) 1 packet
BLK[1]= Channel #
!Rest of argument block depends on function selected
Function: 1 2,3 4,5 6 7
BLK[2]= STATUS* Local NPD . MESTYPE* MESTYPE
BLK[3]= Local NPD* Remote NPD . . .
BLK[4]= Remote NPD* . . . .
* indicates returned value
NPD[0]= Node number or -1 for any
NPD[1]= length of task name (in words)
NPD[2...]= Task name (ASCII)
STATUS=
0: Idle
1: Passive (Waiting for Connect Initiate)
2: Waiting for Connect Confirm
3: Active
4: Waiting for Disconnect Confirm
)%
!
! Formal Parameters
!
BEGIN
MAP FILBLK: REF NDB; !TSK file block
MAP TSKBLK: REF VECTOR; !Argument block for TSK. UUO
LOCAL SAVE_DAP_CODE; !UUO clobbers it if it fails
REGISTER
FF; !These UUOs like a register too.
TSKBLK[1]=.FILBLK[FILE$CHANNEL]; !Get channel #
SAVE_DAP_CODE=.TSKBLK[2]; !Save this away...
DO BEGIN
TSKBLK[2]=.SAVE_DAP_CODE;
FF<LH>=.LEN; !Length of arg blk
FF<RH>=TSKBLK[0]; !Addr
IF CALLI(FF,TSK_)
THEN RETURN .TSKBLK[2] !Success, return DAP code
ELSE BEGIN
IF .FF EQL TS_UDW !I/O-type error, or not finished yet
THEN FF=.TSKBLK[2] AND %O'760000' !Get the status bits
ELSE FF=%O'777777770000'+.FF; !UUO error of some sort
ERR_INTERPRET; !Interpret the error or wait for completion
END
END WHILE 1;
END;
END ELUDOM