This routine works by saving the last word of each buffer load to be processed at the front of the next buffer load. That is so that, when we discover end of it is guaranteed that we have not yet processed the last word of the file. So once we KNOW it is the last word, we can look through it for padding and discard what we find.
It is necessary to call the subroutine GETINI before reading the first character of input.
INBFR: BLOCK INBFL ;This is the buffer. 40 to 200 is
;good for INBFL (160. to 640. characters).
INCNT: 0 ;Number of characters left in buffer
;not fetched yet.
INPTR: 0 ;Byte pointer used for fetching characters
INAHED: 0 ;Look-ahead word.
;The last word of previous bufferful
;saved for the next bufferful.
INEOF: 0 ;-1 if this bufferful is the last.
;Read another input character into A.
;Skip if successful. No skip if EOF.
GETCHR: SOSL INCNT ;Anything left in the buffer?
JRST GETCH1 ;Yes => just get it.
SKIPE INEOF ;If we discovered last time that there
POPJ P, ;is no more, it's eof now.
PUSHJ P,GETBUF ;Fill up the buffer again.
JRST GETCHR
GETCH1: ILDB A,INPTR
AOS (P)
POPJ P,
;Call this to initialize the buffer, before reading the first character.
;This is to ignore the look-ahead word,
;which is garbage the first time around.
GETINI: SETZM INEOF
SETZM INCNT
PUSHJ P,GETCHR ;So just read and throw away
PUSHJ P,GETCHR ;the supposed look-ahead chars.
PUSHJ P,GETCHR
PUSHJ P,GETCHR
PUSHJ P,GETCHR
POPJ P,
GETBUF: PUSH P,A
PUSH P,B
MOVE A,INAHED ;Our previous look ahead word is now
MOVEM A,INBFR ;our first word of input.
MOVE A,[440700,,INBFR] ;That's where we should start fetching.
MOVEM A,INPTR
HRLI A,010700 ;The SIOT should start AFTER that word.
;Note that it is essential that we set up A with
;010700,,INBFR rather than 440700,,INBFR+1
;because of how GETCH4 decrements the byte pointer.
;We are being compatible with SIOT, which also returns
;a byte pointer of the form 010700 rather than 440700.
MOVEI B,INBFL*5-5 ;from the file, but not that word.
;; Do the input. A says where to put it and B says how many chars.
.CALL [ SETZ ? SIXBIT /SIOT/ ? %CLIMM,,INCHAN
A ? 400000,,B]
.LOSE %LSFIL
JUMPE B,GETCH2 ;Didn't get all we asked for =>
SETOM INEOF ; this is the last we will get.
MOVNS B ;B is left with number of characters
ADDI B,INBFL*5 ;we wanted but didn't get.
MOVEM B,INCNT ;Compute how many chars we did get.
;We are now certainly at eof, and the last word of the file
;is now in the buffer.
GETCH4: ADD A,[070000,,] ;Back up to last character.
JUMPL A,GETCH3 ;When we get to left edge of word,
;we have examined the entire last word,
;so there is no more padding.
LDB B,A
CAIE B,^C ;Any number of ^@ and ^C chars is padding.
CAIN B,^@
JRST [ SOS INCNT ;So officially say it is not there.
JRST GETCH4]
CAIN B,^L ;A ^L followed by padding is padding
SOS INCNT ;but nothing before it is padding.
JRST GETCH3
;If we did fill the buffer, we must save one word as look-ahead
;for next time.
GETCH2: MOVE A,INBFR+INBFL-1 ;Save the last word we got.
MOVEM A,INAHED
MOVEI A,INBFL*5-5 ;Don't include those 5 chars
MOVEM A,INCNT ;in the count of how many are in the bfr.
GETCH3: POP P,B
POP P,A
POPJ P,
Both of these examples are suited to reading input from a disk file.
If you are reading input from the terminal, you probably want to do
rubout processing for the user's sake. There is a library you can use
for this; read the file SYSENG;RUBOUT >.