;
; NAME: TIMEIN.M68
;
OBJNAM .SBR
;
; FUNCTION: This routine is used to obtain input from the job's
; terminal input buffer, with the option of timing out if a given
; number of seconds passes following the last received character.
;
; CALLING SEQUENCE:
; XCALL TIMEIN,OPTION,BUFFER,LENGTH where
; Name Type Use
; OPTION Float describes type of timing to be done--
; -1 => don't timeout, i.e., wait until the buffer
; is completely full or hell freezes over
; 0 => return as soon as the input buffer is empty
; (or the caller's buffer is full)
; >0 => timeout if this many seconds passes without
; a character being received
; BUFFER Any This is where the characters are returned. The
; size of this field implies the maximum # of
; bytes that can be returned.
; LENGTH Float The # of characters received is returned in this
; field.
;
; AUTHOR: Tom Dahlquist
;
; EDIT HISTORY:
; When Who What
; 11/16/84 TAD Written.
; 05/03/85 TAD Use timer queue rather than looping on SLEEP command
; to do timing.
;
SEARCH SYS
SEARCH SYSSYM
SEARCH TRM
;
; Map of timer queue block usage
;
ASECT
TLINK: BLKL 1 ; used by system...-> next in q
TCOUNT: BLKL 1 ; used by system...timer count
TADDR: BLKL 1 ; address of routine to execute
TJCB: BLKL 1 ; address of our JCB
TFLAG: BLKL 1 ; address of flag to set on timeout
PSECT
VMAJOR=1
VMINOR=0
VEDIT=0
TIMEIN: PHDR -1,PV$RSM!PV$WSM,PH$REU!PH$REE
CMPW (A3)+,#3 ; must be exactly three args...
JNE RETURN ; die if not.
MOVW (A3)+,D6 ; first arg must be float...
ANDW #7,D6
CMPW D6,#4
JNE RETURN
MOV @A3,A0 ; A0 -> float #
FFTOL @A0,D0 ; D0 used from here on to contain
; value of option...
LEA A3,12(A3) ; skip to address of buffer...
MOV (A3)+,A0 ; A0 -> buffer...
MOV (A3)+,D2 ; D2 = length of buffer...
DEC D2 ; D2 = length of buffer - 1...
MOVW (A3)+,D6 ; test type of last arg...
ANDW #7,D6 ; must be float...
CMPW D6,#4
JNE RETURN
JOBIDX A6 ; keep address of trmdef in A5...
MOV JOBTRM(A6),A5
TST D0 ; OK, go to routine for each type of call
BMI FOREVR ; don't return until buffer full...
BEQ NOWAIT ; return as soon as input buffer emtpy...
;
; OK, we are timing. We set up a timer queue block. Then we loop
; looking for characters. When the input buffer is empty, we go to
; sleep via a JWAIT. When we are woken up from it, one of two things
; has happened: 1) we have received a character, or 2) we have timed
; out. We can tell which happened by checking a flag byte. If we timed
; out, we must check how much time has elapsed since the last
; character was received. If it is greater than our timeout number,
; then we return to the caller. If less, we requeue the timer block
; with a new value.
;
CALL SETQ ; initiate timer Q block...
TIMTCK: TST T.ICC(A5) ; any chars for us?
BEQ NOPE ; br if not...
TIMKBD: KBD ; if so, get it,
MOVB D1,(A0)+ ; store it,
INC D3 ; count it,
DBF D2,TIMTCK ; and see if buffer full.
CALL UNSETQ ; get rid of timer Q block...
BR STOREL ; and leave.
NOPE: GTIMEI D4 ; D4 = current time...
NOPE2: TST T.ICC(A5) ; check again, if anything now there
BNE TIMKBD ; go get it.
JWAIT: JWAIT J.TIW ; sleepy time...
TSTB @A4 ; did we time out?
BEQ TIMKBD ; go get char if not, else
GTIMEI D6 ; D6 = current time
SUB D4,D6 ; D6 = time elapsed since last char
MOV D0,D7 ; D7 = timeout seconds
SUB D6,D7 ; D7 = difference
BLE TIMOUT ; if zero or neg, we timed out
MUL D7,#10000. ; else, requeue block with
MOV D7,TCOUNT(A1) ; new count...
CLRB @A4 ; clear out flag byte...
TIMER @A1 ; requeue it...
BR NOPE2 ; go back to sleep.
TIMOUT: QRET A1 ; get rid of Q block...
BR NOWAIT ; and leave.
;
; This routine just waits until the caller's buffer is full.
;
FOREVR: KBD ; get next char,
MOVB D1,(A0)+ ; store it,
INC D3 ; increment counter,
DBF D2,FOREVR ; and loop until full.
BR STOREL
;
; This routine just returns whatever is in the input buffer.
;
NOWAIT: TST T.ICC(A5) ; anything there?
BEQ STOREL ; return if not...
KBD ; ok, get it,
MOVB D1,(A0)+ ; store it,
INC D3 ; count it,
DBF D2,NOWAIT ; and loop
BR STOREL
;
; Store character count and return.
;
STOREL: MOV @A3,A0 ; A0 -> length variable
FLTOF D3,@A0 ; store counter...
RETURN: RTN
;
; Set up a timer Q block and start the clock ticking.....
;
SETQ: QGET A1 ; get a system Q block...
BNE DIEQ ; die if none...
JOBIDX A6 ; A6 -> our JCB...
MOV A6,TJCB(A1)
MOV D0,D7
MUL D7,#10000. ; convert secs to timer ticks...
MOV D7,TCOUNT(A1)
LEA A6,WAKEME ; A6 -> timer exit routine...
MOV A6,TADDR(A1)
MOV A4,TFLAG(A1) ; A4 -> flag byte (in BASIC area)
CLRB @A4 ; clear flag byte...
TIMER @A1 ; start timer...
RTN
DIEQ: EXIT
;
; Come here when we have received enough characters and want to get
; rid of our timer Q entry. This is complicated due to three
; possibilities: 1) we don't find it in the timer Q--don't do
; anything; 2) we find it and it is the first entry--we stick a
; substitute exit routine into the Q block itself which just returns
; the block to the system when called; 3) we find it and it is not the
; first block in the Q--we remove it, add its time to the next block
; in the Q, and return it to the system.
;
UNSETQ: SUPVR
SVLOK
MOV TIMQUE,D6 ; A6 -> first timer Q block...
BEQ DONEDQ ; if none, all done.
MOV D6,A6
CMP A6,A1 ; ours first?
BEQ CHGQ ; go change it if so...
DQLOOP: MOV A6,A2 ; A2 -> previous Q block...
MOV @A6,D6
BEQ DONEDQ ; if no more, all done.
MOV D6,A6
CMP A6,A1 ; ours?
BNE DQLOOP ; if not, go get next...
MOV @A1,A6 ; A6 -> next block in Q...
MOV A6,@A2 ; remove us from Q, and
BEQ 1$ ; if there is a next block we
MOV TCOUNT(A1),D6
ADD D6,TCOUNT(A6) ; add our timer count to its.
1$: QRET A1 ; return Q block to system...
DONEDQ: LSTS #0 ; user mode, allow interrupts
RTN
CHGQ: MOV #DEQL-1,D1 ; length of dummy exit routine...
LEA A6,TJCB(A1) ; where to move it...
MOV A6,TADDR(A1)
LEA A2,DEQSTF
CQLOOP: MOVB (A2)+,(A6)+
DBF D1,CQLOOP
LSTS #0 ; user mode, allow interrupts
RTN
DEQSTF: SUB #14,A0
QRET A0
RTN
DEQL = .-DEQSTF
;
; This is the timer exit routine. It sets the flag byte and returns.
;
WAKEME: MOV TFLAG-14(A0),A6 ; A6 -> flag byte...
SETB @A6 ; set flag...
MOV TJCB-14(A0),A0 ; A0 -> our JCB...
JRUN J.TIW+J.NXT ; run job...
RTN