; ; 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 MOV D2,D6 ; D6 = length of buffer - 1... MOV A0,A6 ; A6 -> buffer... CLEAR: CLRB (A6)+ ; clear out return buffer... DBF D6,CLEAR CLR D3 ; D3 used to count chars received... 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 END .