.title USERMN User Terminal Monitor .IDENT /X01.03/ .ENABLE LC ; .list me ; .list meb .nlist cnd .sbttl Introduction ; ; USERMN - User Terminal Activity Monitor for RSX-11M/M+ ; ; ; Author: ; Bruce R. Mitchell ; ; Patterned after the POOL monitor task supplied by Digital ; Equipment Corporation on RSX-11M-PLUS V1.0 Autopatch E. ; ; ; Source Site: ; Engineering Systems and Technology Laboratory ; 3M Company, 3M Center, St. Paul, Minnesota 55144 ; ; Revision History: ; ; 31-dec-81 First version written ; 3-jan-82 Command file generates Ascii strings for time ; 5-jan-82 In-house version released ; 9-jan-82 Decnet HT: support conditionally added ; 11-jan-82 Decus version released ; 20-jan-82 VT100 support conditionalized ; 27-feb-82 Warning messages cleared for active terminals ; 15-mar-82 Correct conditionlization for HT's ; 21-Jun-82 Modified to fit CBD's 11/44 system ; First warning sets terminal no-priv ; Added software to log off TT0: ; by using 'ghost' tasks ; Added loop counter ; 25-aug-82 resets non-logged in terminals to the ; non-logged in CLI (lgocli) ; ; .page .sbttl Macro calls and Definitions ; ; System definition macros from LB:[1,1]EXEMC.MLB ; .mcall dcbdf$ ; Device control block offsets .mcall hwddf$ ; Hardware registers .mcall tcbdf$ ; Terminal control block offsets .mcall ucbdf$ ; Unit control block offsets dcbdf$ hwddf$ tcbdf$ ucbdf$ ; ; ; Directive macros from LB:[1,1]SYSLIB.OLB ; .mcall alun$ ; assign logical unit number .mcall dir$ ; execute directive .mcall gtim$ ; get system clock tiem .mcall mrkt$ ; mark time (wait) .mcall qiow$ ; queue i/o and wait .mcall spwn$ ; spawn task .mcall stse$ ; stop for single event flag .mcall wtse$ ; wait for single event flag .mcall scli$ ; set cli ; .page .sbttl Local Variables ; ; ; Local variables ; lopcnt: .word 0 ; loop counter ; ; datbuf: .blkw 8. ; Buffer area for date and time ; ; ; local terminals data storage area ; ; format: Flag word - idle counter ; ; TT offsets TT offsets ; ; 0 0 1 4 ; 2 10 3 14 ...... ; ; trmdat: ; beginning of data storage area .rept ntt ; repeat for number of terminals .word 0 ; terminal flags .word 0 ; idle time counter .endr .if df rs.nsl ; if number of HT: terminals is defined ; ; ; decnet terminals data storage area ; netdat: ; beginning of data storage area .rept nht ; repeat for number of terminals .word 0 ; terminal flags .word 0 ; idle time counter .endr .endc ; ; .page .sbttl Data Structures ; ; directive data structures ; ; asslun: alun$ 2, TT, 0 ; lun for tty warning or logout message datdpb: gtim$ datbuf ; dpb for current time timdpb: mrkt$ efn2,secnds,2 ; wait for the monitoring interval brodpb: qiow$ io.wbt,lun2,efn1,,,,< , > ; print messages on terminals condpb: qiow$ io.wbt,lun1,efn1,,,, ; console message ; spawn MCR to log out the terminal at address MCRTRM, setting ; event flag SPNEFN on exit or status emmission mcrspn: spwn$ MCR...,,,,,spnefn,,,mcrbye,byelth,,TT mcrbye: .ascii /bye/ ; MCR command to logout terminal byelth = . - mcrbye ; length of command .even setspn: spwn$ MCR...,,,,,spnefn,,,mcrset,setlth,,TT mcrset: .ascii \set /nopriv=ti:\ ; MCR command to de-privilege terminal setlth = . - mcrset ; length of command .even lgoset: scli$ LGOCLI,TT,0 sleep: stse$ efn2 ; goto sleep and wait until next check time spnwai: wtse$ spnefn ; wait for spawn or timeout event flag ; ; ; ghosts = 3 ; number of tasks the are NOT considered active ghstnm: .rad50 /COT.../ ; name of the first task to ignore .rad50 /BIGBRO/ ; ignore us .rad50 /MCR.../ ; and lets forget this too! ; ; .page .sbttl Messages ; ; .nlist bin ; conmsg: .ascii ; time: .blkb 8. ; storage for time string .ascii / / ; separating spaces hedlen = . - conmsg ; length of header area .ascii /Idle terminal forced logout on / .even lgodev: .blkw 1 ; space to print terminal type lgotrm: .blkb 2 ; space to print terminal number .ascii /:/ ; conlen = . - conmsg ; warn1: .ascii .ascii /From BIG BROTHER: First idle terminal warning!/<07> .ascii / / .if df v$t100 ; if vt100 support .ascii <133><65><73><61><155> ; esc[5;1m (bold and blink) .endc .even .word time1 ; remaining time .ascii / minutes/ .if df v$t100 .ascii <133><60><155> ;esc[0m .endc .ascii / before forced logout./ .ascii wrn1ln = . - warn1; ; ; .even warnp: .ascii / Terminal now non-privileged/<07> .ascii wrnpln = . - warnp .even warn2: .ascii .ascii /From BIG BROTHER: Second idle terminal warning!/<07> .ascii / / .if df v$t100 ; if vt100 support .ascii <133><65><73><61><155> ; esc[5;1m (bold and blink) .endc .even .word time2 ; remaining time .ascii / minutes/ .if df v$t100 .ascii <133><60><155> ;esc[0m .endc .ascii / before forced logout./ wrn2ln = . - warn2; ; ; .even warn3: .ascii .ascii /From BIG BROTHER: Third idle terminal warning!/ .ascii / / .if df v$t100 ; if vt100 support .ascii <133><65><73><61><155> ; esc[5;1m (bold and blink) .endc .even .ascii / 1.0000 minute/ .if df v$t100 .ascii <133><60><155> ;esc[0m .endc .ascii / before forced logout./ wrn3ln = . - warn3; ; ; .even banzai: .ascii .if df v$t100 .ascii <133><61><73><61><110> ; esc[1;1H .ascii /#9/ ; esc#9 .ascii /[>3;11;0;14;132J/ ; clear lines 11-14 inclusive .ascii <133><65><155> ; esc[5m .ascii /[12;26H/ ; move to row 12, col 26 .endc .ascii /From BIG BROTHER: BYE, BYE/ .if df v$t100 .ascii /[13;26H/ .endc .ascii / -- Idle terminal logout/ .if df v$t100 .ascii <133><60><155> ; esc[0m .endc .ascii banzln = . - banzai ; ; .even .list bin .page .sbttl .sbttl Mainline Code .sbttl .sbttl TSKS Search active task list ; ; ; Mainline code ; usermn:: tsks: mov #,r0 ; r0 counts down TT: data blocks 10$: bic #,trmdat(r0) ; clear terminal active flags sub #4,r0 ; point at flag for preceding unit bge 10$ ; if not passed TT0:, loop again .if df rs.nsl mov #,r0 ; r0 counts down HT: data blocks 20$: bic #,netdat(r0) ; clear terminal active flags sub #4,r0 ; point at flag for preceding unit bge 20$ ; if not passed HT0:, loop again .endc ; ; switch to system state and check active task list ; call $SWSTK,compar ; switch to system state ; and return at compar mov $acthd,r0 ;; r0 points to active task listhead 23$: mov #ghstnm,r3 ;; get basis of ghost names mov #ghosts,r1 ;; get number of ghosts beq 30$ ;; ignore if no ghosts 25$: cmp (r3)+,t.nam(r0) ;; is this task a ghost? beq 26$ ;; maybe, first three letters match inc r3 ;; no, skip second word (three letters) inc r3 ;; ( by two bytes) br 27$ ;; and then go to the next ghost 26$: cmp (r3)+,t.nam+2(r0) ;; do the second three letters match? beq 70$ ;; yes, so ignore it 27$: sob r1,25$ ;; no, so try again... 30$: mov t.ucb(r0),r3 ;; r3 points to UCB for this task mov u.dcb(r3),r1 ;; r1 points to DCB for the UCB cmp d.nam(r1),#"TT ;; is this a terminal dcb? .if ndf rs.nsl bne 70$ ;; not a TT:, and no HT:'s so skip .iff bne 40$ ;; not a TT:, but now check HT:'s clr r4 ;; r4 clear -> TT: br 50$ ;; and go process it 40$: cmp d.nam(r1),#"HT ;; is this a decnet terminal? bne 70$ ;; no, so we don't care.... inc r4 ;; indicate an HT: .endc ; calculate terminal number and set active task flag 50$: sub d.ucb(r1),r3 ;; r3 is distance of UCB from 1st UCB clr r2 ;; clear 32-bit extension word div d.ucbl(r1),r2 ;; divide distance by length of UCB movb d.unit(r1),r1 ;; r1 is lowest unit # on DCB add r1,r2 ;; r2 is now the terminal number asl r2 ;; multiply r2 by 4 for addressing asl r2 ;; (2 bytes/word * 2 words) .if ndf rs.nsl ; if decnet not supported bis #tm.tsk,trmdat(r2) ;; set active task flag for TT: .iff tst r4 ;; HT: or TT: bne 60$ ;; if an HT:, go process it bis #tm.tsk,trmdat(r2) ;; if a TT:, take care of it br 70$ ;; next task 60$: bis #tm.tsk,netdat(r2) ;; take care of HT: .endc ; set up pointers for next active task and check for list end 70$: mov t.actl(r0),r0 ;; point to next active task tst r0 ;; is this the last task? bne 23$ ;; no, so get more .page .sbttl DCBs search DCB list ; ; now we know which tty's have tasks. next thing to do is ; find the logged in tty's. but before that, clear the flags ; DCBS: mov $devhd,r0 ;; ro points to DCB listhead 10$: cmp d.nam(r0),#"TT ;; is this a TT: DCB? .if ndf rs.nsl ; if no decnet bne 70$ ;; not a TT:, skip to new DCB .iff bne 20$ ;; not a TT:, check for HT: clr r4 ;; r4 clear -> TT: br 30$ ;; and go process TT: 20$: cmp d.nam(r0),#"HT ;; is this an HT: DCB? bne 70$ ;; no, skip to new DCB inc r4 ;; r4 not clear -> HT: .endc 30$: movb d.unit(r0),r1 ;; r1 is lowest unit number on DCB movb d.unit+1(r0),r3 ;; r3 is highest unit number sub r1,r3 ;; r3 is number of units served - 1 inc r3 ;; r3 is number of units served ;; and usable as down counter mov r1,r2 ;; r2 is lowest unit number of DCB asl r2 ;; multiply it by 4 for addressing asl r2 ;; ( 2 bytes/word * 2 words) mov r1,r5 ;; and hold unit number too mov d.ucb(r0),r1 ;; r1 points to first terminal's UCB ; loop through all units on this DCB, checking for logged-in units 40$: bit #u2.log,u.cw2(r1) ;; is this unit logged-in? bne 60$ ;; no, so get another one .if ndf rs.nsl 45$: bis #tm.log,trmdat(r2) ;; set logged-in flag .iff 45$: tst r4 ;; TT: or HT: bne 50$ ;; if an HT:, go to it bis #tm.log,trmdat(r2) ;; set flag for logged-in TT: br 55$ ;; go for next unit 50$: bis #tm.log,netdat(r2) ;; set flag for logged-in HT: .endc 55$: bit #u2.prv,u.cw2(r1) ;; is this unit priv'ed beq 60$ ;; no, look at next unit .if ndf rs.nsl bis #tm.prv,trmdat(r2) ;; set priv'ed flag .iff tst r4 ;; TT: or HT: bne 555$ ;; if an HT:, go to it bis #tm.prv,trmdat(r2) ;; set flag for priv'ed TT: br 60$ ;; go for next unit 555$: bis #tm.prv,netdat(r2) ;; set flag for priv'ed HT: .endc 60$: add #4,r2 ;; r2 points at next terminal flag word inc r5 add d.ucbl(r0),r1 ;; r1 points at next UCB on DCB sob r3,40$ ;; loop for each UCB on DCB ; finish with DCB, prepare for next DCB 70$: mov d.lnk(r0),r0 ;; r0 now points at next DCB in list bne 10$ ;; if more, check them out return ;; return to user state .page .sbttl COMPAR crosscheck tasks and terminals ; ; back in user state. check task vs. logged-in. bump idle counter ; for logged-in, no task terminals and reset idle counter for those ; that have active tasks. ; compar: mov #,r0 ; r0 counts down terminal data blocks mov #itt,r5 ; r5 counts down terminal number 10$: bit #tm.log,trmdat(r0) ; is this terminal logged-in? bne 20$ ; if so, skip and process it mov #"TT,lgoset+s.cidv ; set SCLI$ DPB to TT's mov r5,lgoset+s.ciun ; set SCLI$ DPB unit dir$ #lgoset ; set not logged in terminal to lgocli clr trmdat+2(r0) ; not logged-in, clear idle counter br 40$ ; and set up for next data block ; terminal logged in; check for active task 20$: bit #tm.tsk,trmdat(r0) ; does it have and active task? bne 30$ ; if so, skip 'no task' code inc trmdat+2(r0) ; no active task -> bump idle counter br 40$ ; and set up for next data block 30$: clr trmdat+2(r0) ; active task, clear idle counter bic #,trmdat(r0) ; clear warning flags ; set up for next TT: data block 40$: sub #4,r0 ; point at flag word of preceding unit dec r5 bge 10$ ; more still to go.. .if df rs.nsl mov #,r0 ; r0 counts down HT data blocks mov #iht,r5 50$: bit #tm.log,netdat(r2) ; is this HT logged-in? bne 60$ ; if so, skip and process it mov #"HT,lgoset+s.cidv ; set SCLI$ DPB to HT's mov r5,lgoset+s.ciun ; set SCLI$ DPB unit dir$ #lgoset ; set not logged in terminal to lgocli clr netdat+2(r0) ; not logged-in, clear idle counter br 80$ ; and set up for next data block ; HT logged in; check for active task 60$: bit #tm.tsk,netdat(r0) ; does it have and active task? bne 70$ ; if so, skip 'no task' code inc netdat+2(r0) ; no active task -> bump idle counter br 80$ ; and set up for next data block 70$: clr netdat+2(r0) ; active task, clear idle counter bic #,netdat(r0) ; clear warning flags ; set up for next HT: data block 80$: sub #4,r0 ; point at flag word of preceding unit dec r5 bge 50$ ; more still to go.. .endc .page .sbttl CKTIME check terminal idle time ; ; check terminals with nonzero idle counters for log-off ; cktime: clr r1 ; r1 is terminal number clr r2 ; r2 is offset in terminal data block .if df rs.nsl clr r3 ; assume terminal type is TT: .endc mov #"TT,asslun+a.luna ; set device to TT for alun$ mov #"TT,mcrspn+s.pwdn ; set device to TT for spwn$ mov #"TT,lgodev ; set device to TT for console message 10$: tst trmdat+2(r2) ; is this an idle terminal? beq 60$ ; yes, set up for next terminal ; terminal is idle - do it to it cmp trmdat+2(r2),#first ; idle less than first warning time? bge 20$ ; if not, do something br 60$ ; if so, don't ; check against logout time limit 20$: cmp trmdat+2(r2),#fourth ; past logout time? blt 30$ ; not quite call killit ; goody,goody!!! br 60$ ; and go for another terminal ; check against final warning time limit 30$: cmp trmdat+2(r2),#third ; final warning? blt 40$ ; not quite call warfin ; good,good br 60$ ; and go for another terminal ; check against second warning time limit 40$: cmp trmdat+2(r2),#second ; second warning? blt 50$ ; not quite call wartwo ; ok,ok br 60$ ; and go for another terminal ; issue first warning 50$: call warone ; weellll, better than nothing ; check for last terminal and setup for next 60$: cmp r1,#itt ; last terminal? .if ndf rs.nsl bge 140$ ; yes, so exit loop .iff bge 70$ ; yes, so do HT:'s .endc inc r1 ; next terminal number add #4,r2 ; next offset br 10$ ; and go for it .if df rs.nsl 70$: clr r1 ; r1 is HT: number clr r2 ; r2 is offset in HT: data block mov #1,r3 ; assume HT: type is HT: mov #"HT,asslun+a.luna ; set device to HT for alun$ mov #"HT,mcrspn+s.pwdn ; set device to HT for spwn$ mov #"HT,lgodev ; set device to HT for console message 80$: tst netdat+2(r2) ; is this an idle HT: terminal? beq 130$ ; yes, set up for next HT: ; HT: is idle - do it to it cmp netdat+2(r2),#first ; idle less than first warning time? bge 90$ ; if not, do something br 130$ ; if so, don't ; check against logout time limit 90$: cmp netdat+2(r2),#fourth ; past logout time? blt 100$ ; not quite call killit ; goody,goody!!! br 130$ ; and go for another HT: ; check against final warning time limit 100$: cmp netdat+2(r2),#third ; final warning? blt 110$ ; not quite call warfin ; good,good br 130$ ; and go for another HT: ; check against second warning time limit 110$: cmp netdat+2(r2),#second ; second warning? blt 120$ ; not quite call wartwo ; ok,ok br 130$ ; and go for another HT: ; issue first warning 120$: call warone ; weellll, better than nothing ; check for last HT: and setup for next 130$: cmp r1,#iht ; last HT:? bge 140$ ; yes, so exit loop inc r1 ; next HT: number add #4,r2 ; next offset br 80$ ; and go for it .endc ; all terminals processed, back to sleep 140$: dir$ #timdpb ; set wake-up call inc lopcnt ; say we finished another iteration mov lopcnt,r0 ; dir$ #sleep ; and go to sleep jmp tsks ; rise and shine and do it all again .page .sbttl .sbttl subroutines .sbttl .sbttl datsub create ascii time ; ; datsub - get current time and convert to ascii ; ; inputs - none ; ; outputs - time gets ascii time ; ; all registers preserved ; datsub: mov r0,-(sp) mov r1,-(sp) mov r2,-(sp) dir$ #datdpb ; execute date-time directive mov #time,r0 ; load ascii time field address in r0 mov #datbuf+g.tihr,r1 ; load binary time field address in r1 mov #3,r2 ; specify HH:MM:SS format call $tim ; convert to ascii mov (sp)+,r2 mov (sp)+,r1 mov (sp)+,r0 return ; return to caller .page .sbttl killit log out idle terminal ; ; killit log out idle terminal ; ; inputs: r1 - number of target terminal ; r2 - pointer to terminal block ; r3 - 0 -> TT:, 1 -> HT: ; ; outputs: terminal flag word is cleared ; ; registers are not saved and restored ; ; killit: mov r1,mcrspn+s.pwvt ; load terminal number in spwn$ DPB dir$ #mcrspn ; say BYE-BYE!!! bcc 5$ ; if ok, continue return ; if not ok, punt 5$: dir$ #spnwai ; wait for completion mov r1,asslun+a.lunu ; load terminal number ins alun$ DPB dir$ #asslun ; assign lun 2 to target terminal bcc 10$ ; if ok, issue message return ; if not, punt ; log forced logout of the target terminal 10$: mov #banzai,brodpb+q.iopl ; load message address into qio DPB mov #banzln,brodpb+q.iopl+2 ; load message length into qio DPB dir$ #brodpb ; send logout message .if df rs.nsl tst r3 ; is this a TT: or HT: beq 15$ ; if TT:, skip HT: code clr netdat(r2) ; clear all HT: flags br 20$ ; and continue .endc 15$: clr trmdat(r2) ; clear all flags for TT: ; log forced logout of the target terminal on console 20$: call datsub ; get message time and date mov r0,-(sp) ; save r's mov r1,-(sp) ; mov r2,-(sp) ; mov #0,lgotrm ; null fill terminal number mov #lgotrm,r0 ; move address of output field to r0 clr r2 ; specify suppression of leading 0's call $cbomg ; convert binary TT to ascii mov (sp)+,r2 mov (sp)+,r1 mov (sp)+,r0 dir$ #condpb ; and send out message return ; and back to caller .page .sbttl warone issue first warning ; ; warone - issue first warning and de-privilege the terminal ; ; inputs: r1 - target terminal number ; r2 - data block offset ; r3 - 0 -> TT:, 1 -> HT: ; ; outputs: terminal flag word is updated ; ; registers are not save or restored ; .if df rs.nsl warone: tst r3 ; is this an HT:? beq 5$ ; a TT: bit #tm.1st,netdat(r2) ; an HT:, has first message been sent? beq 10$ ; no, so send it return ; yes, so don't bother 5$: bit #tm.1st,trmdat(r2) ; a TT:, has first message been sent? .iff warone: bit #tm.1st,trmdat(r2) ; has the first message been sent? .endc beq 10$ ; no, so issue it return ; yes, don't do it again 10$: mov r1,asslun+a.lunu ; load terminal number in alun$ DPB dir$ #asslun ; assign lun 2 to target terminal bcc 20$ ; if it succeeded, issue message return ; if failed, punt 20$: mov #warn1,brodpb+q.iopl ; load message address into qio DPB mov #wrn1ln,brodpb+q.iopl+2 ; load message length into qio DPB dir$ #brodpb ; send out message .if df rs.nsl tst r3 ; is this a TT:, or an HT: beq 25$ ; TT:, so go to TT: code bis #tm.1st,netdat(r2) ; HT:, set first message sent flag bit #tm.prv,netdat(r2) ; was HT: priv'ed beq 40$ ; no, so leave br 30$ ; yes, rejoin common code .endc 25$: bis #tm.1st,trmdat(r2) ; TT:, set first message sent flag bit #tm.prv,trmdat(r2) ; TT:, was TT: priv'ed beq 40$ ; no, so go home 30$: mov r1,setspn+s.pwvt ; load terminal number into spwn$ DPB dir$ #setspn ; de-privilege terminal bcc 35$ ; if ok, good return ; if not, punt 35$: dir$ #spnwai ; wait for spawn to complete mov #warnp,brodpb+q.iopl ; load message address into qio DPB mov #wrnpln,brodpb+q.iopl+2 ; load message length into qio DPB dir$ #brodpb ; send out message 40$: return ; and go home .page .sbttl wartwo issue first warning ; ; wartwo - issue second warning ; ; inputs: r1 - target terminal number ; r2 - data block offset ; r3 - 0 -> TT:, 1 -> HT: ; ; outputs: terminal flag word is updated ; ; registers are not save or restored ; .if df rs.nsl wartwo: tst r3 ; is this an HT:? beq 5$ ; a TT: bit #tm.2nd,netdat(r2) ; an HT:, has second message been sent? beq 10$ ; no, so send it return ; yes, so don't bother 5$: bit #tm.2nd,trmdat(r2) ; a TT:, has second message been sent? .iff wartwo: bit #tm.2nd,trmdat(r2) ; has the second message been sent? .endc beq 10$ ; no, so issue it return ; yes, don't do it again 10$: mov r1,asslun+a.lunu ; load terminal number in alun$ DPB dir$ #asslun ; assign lun 2 to target terminal bcc 20$ ; if it succeeded, issue message return ; if failed, punt 20$: mov #warn2,brodpb+q.iopl ; load message address into qio DPB mov #wrn2ln,brodpb+q.iopl+2 ; load message length into qio DPB dir$ #brodpb ; send out message .if df rs.nsl tst r3 ; is this a TT:, or an HT: beq 25$ ; TT:, so go to TT: code bis #tm.2nd,netdat(r2) ; HT:, set second message sent flag br 30$ ; and rejoin common code .endc 25$: bis #tm.2nd,trmdat(r2) ; TT:, set second message sent flag 30$: return ; and go home .page .sbttl warfin issue final warning ; ; warfin - issue final warning ; ; inputs: r1 - target terminal number ; r2 - data block offset ; r3 - 0 -> TT:, 1 -> HT: ; ; outputs: terminal flag word is updated ; ; registers are not save or restored ; .if df rs.nsl warfin: tst r3 ; is this an HT:? beq 5$ ; a TT: bit #tm.3rd,netdat(r2) ; an HT:, has third message been sent? beq 10$ ; no, so send it return ; yes, so don't bother 5$: bit #tm.3rd,trmdat(r2) ; a TT:, has third message been sent? .iff warfin: bit #tm.3rd,trmdat(r2) ; has the third been sent? .endc beq 10$ ; no, so issue it return ; yes, don't do it again 10$: mov r1,asslun+a.lunu ; load terminal number in alun$ DPB dir$ #asslun ; assign lun 2 to target terminal bcc 20$ ; if it succeeded, issue message return ; if failed, punt 20$: mov #warn3,brodpb+q.iopl ; load message address into qio DPB mov #wrn3ln,brodpb+q.iopl+2 ; load message length into qio DPB dir$ #brodpb ; send out message .if df rs.nsl tst r3 ; is this a TT:, or an HT: beq 25$ ; TT:, so go to TT: code bis #tm.3rd,netdat(r2) ; HT:, set third message sent flag br 30$ ; and rejoin common code .endc 25$: bis #tm.3rd,trmdat(r2) ; TT:, set third message sent flag 30$: return ; and go home .end usermn