Trailing-Edge
-
PDP-10 Archives
-
custsupcuspmar86_bb-x130b-sb
-
sosbuf.mac
There are 3 other files named sosbuf.mac in the archive. Click here to see a list.
TITLE SOSBUF -- BUFFER HANDLING ROUTINES
SUBTTL William R. Sears, III Jan 1976
; This file contains the buffer handling routines. Included
; are the temporary file IO routines, routines to locate
; a specific line in the file and code to add and delete lines
; from the file.
SEARCH SOSTCS
$INIT
; The following macro is for debugging only
; It should be redefined to <> for a production SOS.
FTDEBUG==0
IFN FTDEBUG,<
DEFINE CKPTR.,<
PUSHJ P,CKPNTR
>>
IFE FTDEBUG,<DEFINE CKPTR.,<>>
; The following routine(s) are now obsolete.
SUBTTL Buffer Description
comment `
The buffer is organized as a large block of storage divided into
3 parts. The buffer will always be a multiple of BLKSIZ*2 (256)
words. It is laid out as follows:
------------------------------------
[BUFFIR]------>| |
| |
| |
| Lines occuring before the |
| current line |
| |
| |
| |
------------------------------------
[LPNTR]------->| |
| |
\ /
/ Free Space \
\ /
| |
| |
------------------------------------
[PNTR]-------->| First word of Current Line |
| |
| |
| |
------------------------------------
[BUFLAS]------> First word following buffer data
| . |
| . |
| . |
[BUFLIM]------>| Highest value BUFLIM can take on |
------------------------------------
`
CONT.
comment `
(The following comments use the BLISS notation of .X to mean the
contents of location X.)
The buffer data base consists of:
BUFFIR Contains the address of the first data word in the buffer.
BUFLOW Contains a copy of BUFFIR with -200 in the left half. It
is used for compares with LPNTR to check for buffer empty.
BUFBDY Contains the low order seven bits of BUFFIR. It is used to
detect block boundaries for line blocking the output file.
BUFLAS Contains the address of the word following the last data word
in buffer. Will always be equal to BUFLIM unless EOF
flag is on.
BUFLIM Contains the highest value that BUFLAS may take on.
PNTR Contains the address of the first word of the current line.
It is the first word of the high part of the buffer.
LPNTR Contains the address of the first word in the free space area.
The left half contains a negative count of words to go
before the next block boundary.
BUFHSZ Is the number of words given by (bufsiz-2*blksiz)/2 rounded
down to a multiple of whole blocks.
The size of the buffer is given by .BUFLIM-.BUFFIR
Reading is always performed to the high part of the buffer, IO always
starts at .BUFLAS.
The number of data words in the high part is given by .BUFLAS-.PNTR.
The number of data words in the low part is given by .LPNTR-.BUFFIR.
The number unused words in the buffer is given by .PNTR-.LPNTR.
The buffer is completely full when .PNTR EQL .LPNTR AND
.BUFLAS EQL .BUFLIM.
Writing is always performed from the low part of the buffer. IO always
starts from .BUFFIR.
`; End of comment
comment `
CHANNEL TABLE
The channel table contains a one word entry for each channel used
by SOS for input or output. It is indexed by the logical channel
number. The format of the table is:
8 12 35
------------------------------------
| 0 | chn | open block | For channel 1
------------------------------------
| 0 | chn | open block | For channel 2
------------------------------------
| 0 | chn | open block | For channel 3
------------------------------------
| 0 | chn | open block | For channel 4
------------------------------------
| 0 | chn | open block | For channel 5
------------------------------------
| 0 | chn | open block | For channel 6
------------------------------------
where
chn channel number used for communication with
the monitor
open block is the address of the open block for this
channel
All IO UUO are redefined in SOS to be local UUO's that do the
channel mapping before calling the monitor. Thus the call
ENTER 3,X
would map logical channel 3 into the channel specified in CHNTAB
before calling the monitor. (The actual monitor call is done with
saved definitions which are given in SOSSYM).
NOTE: Although there exists an entry for the channel TTY, this
channel must not be changed by modifying CHNTAB[TTY]. This is
because the error handler in SOSERR is not properly recursive
and cannot safely do local UUO's while performing error recovery.
For this reason, SOSERR does IO monitor calls for the channel TTY
directly. Do not change CHNTAB[TTY]! Since other parts of SOS
do use local UUO's on channel TTY, the entry in CHNTAB[TTY] must
be setup, however.
`; End of commentary on CHNTAB
SUBTTL Buffer Initialization
; Here from SOSINT to setup the buffer pointers. Call with
; T1=desired buffer size and .JBFF## pointing to the first free word
BUFINI::AOS T2,.JBFF## ; First free word
MOVEM T2,BUFFIR ; Point to start of buffer
HRLI T2,-BLKSIZ ; Count to next block
MOVEM T2,BUFLOW ; Save for limit checks
ANDI T2,BLKSIZ-1 ; Boundary bits
MOVEM T2,BUFBDY ; Save for BLKCHK
ADDI T1,BLKSIZ-1
TRZ T1,BLKSIZ-1 ; Make size multiple of block size
MOVE T2,BUFFIR ; Get first again
ADD T2,T1 ; Compute end of the buffer
MOVEM T2,BUFLIM ; Save as limit
ADDI T2,2 ; Don't use .BUFLIM for data
MOVEM T2,.JBFF## ; And as first free
MOVEM T2,SVJFF2 ; Save this for command processor
MOVEM T2,CXFCOR ; Save for Co-edit return
CAMG T2,.JBREL## ; See if this much core is here
JRST BUFIN0 ; Yes, don't use CORE UUO
CORE T2, ; Request more core
JRST BUFIN1 ; Failed
BUFIN0: MOVE T2,BUFLIM
MOVE T1,CRLF## ; Get a CRLF
MOVEM T1,1(T2) ; Leave at end of buffer for Find
SUB T2,BUFFIR ; Size of the buffer
MOVEI T1,-200(T2) ; Minus one block (save for blocking)
ASH T1,-1 ; Half of it
TRZ T1,BLKSIZ-1 ; Round to whole blocks
MOVEM T1,BUFHSZ ; Save buffer half size
ASH T2,-3 ; Small value
TRZ T2,BLKSIZ-1 ; Round to whole blocks
CAIG T2,BLKSIZ ; Must be at least 1 block
MOVEI T2,BLKSIZ ; Ensure it
MOVEM T2,BUFSAI ; Remember short value
SETZM @BUFLIM ; Clear limit word
MOVE T2,BUFFIR
SETZM -1(T2) ; Clear word preceding buffer
JRST CPOPJ1## ; Wins, return to user
BUFIN1: SOS T2,BUFFIR ; Get back start of space
MOVEM T2,.JBFF## ; Restore JBFF
POPJ P,
SUBTTL FINDx -- Subroutines to move forward or backward 1 line
; FINDN -- Subroutine to find the next line
; FINDN1 -- Subroutine to find the current or next line.
; Call with
; PUSHJ P,FINDN (or FINDN1)
; <always return here with line number in T1>
; Returns T1=0 if end of file; never returns a page mark.
FINDN1::CKPTR.
MOVE T1,(PNTR) ; Fetch current line
TRNE T1,1 ; Pointing to a line number?
POPJ P, ; Yes, just return
;
FINDN:: CKPTR.
PUSHJ P,SETRFL##
MOVE T1,CPGL ; Get current logical page
MOVEM T1,DPG ; Call it desired
MOVE T1,(PNTR) ; Fetch current line number
CAME T1,PGMK ; Is it a page mark?
PJRST FORLIN ; Call FORLIN
PUSHJ P,FORLIN ; Find the next line
AOS CPG ; Increment page counter
JUMPE T1,FIND3 ; Return 0 for end of file, set BGSN
POPJ P, ; and return
; FINDB -- Find previous line
;
FINDB:: CKPTR.
PUSHJ P,SETRFL##
PUSH P,(PNTR) ; Save current line
PUSH P,CPG ; and page
PUSHJ P,BKPLIN ; Try and backup one line
JRST FINDBA ; Nope, must wrap
CAMN T1,PGMK ; Pointing to a page mark?
SOS CPG ; Yes, decrement Page number
ADJSP P,-2
POPJ P, ; Line is now setup
; Here if we have to wraparound to find the previous line
FINDBA: POP P,DPG ; Save desired page
POP P,SINDEX ; Line to re-find
TRO FL,SSBGSN ; Force FIND to wraparound
PUSHJ P,FIND ; Refind the line
SETZ T1, ; Anticipate no line
CAMN LPNTR,BUFLOW ; Already at first line?
POPJ P, ; Yes, just return that
PUSHJ P,BKPLIN ; Backup one line
ERROR ICN ; Huh?
CAMN T1,PGMK## ; Pointing to a page mark?
SOS CPG ; Decrement page number then
POPJ P,
SUBTTL FINDLO, FINDHI and FINDCL
; FINDLO -- Find first line of typed range
; FINDL1 -- Same, but does not call SVLOLN
FINDLO::PUSHJ P,SVLOLN## ; Save for #
FINDL1::MOVE SINDEX,LOLN ; Get line number
MOVE T1,LOPG ; And page
PJRST FINDT1 ; Setup DPG and call FIND
; FINDHI -- Routine to FIND HILN/HIPG
; FINDH1 -- Same, but does not call SVHILN
FINDHI::PUSHJ P,SVHILN## ; Save for #
FINDH1::MOVE SINDEX,HILN ; Get line number
MOVE T1,HIPG ; And page
PJRST FINDT1 ; Setup DPG and call FIND
; FINDCL -- Routine to find the current logical line/page
FINDCL::MOVE SINDEX,CLN ; Current logical line
MOVE T1,CPGL ; and page
FINDT1: MOVEM T1,DPG ; Setup DPG
PJRST FIND ; and call FIND
SUBTTL Routines to insert page marks
; INSPAG -- Routine to insert a new page into the file
; Update CPG and CLN
INSPAG::PUSHJ P,INSPMK ; Insert a page mark into the buffer
MOVE T2,CPG ; Current page
CAIE T1,0 ; End of file
CAIN T1,PGMK ; or page mark
MOVE T1,LNZERO## ; Use line zero
DMOVEM T1,CLN ; Store current line and page
POPJ P,
; INSPMK -- routine to insert a page mark into the file
INSPMK::DMOVE T1,PGMK ; Load a page mark
DMOVEM T1,LIBUF ; Save in LIBUF
MOVEI T1,2 ; Length of a page mark
PUSHJ P,INSLIN ; Insert the page mark
AOS BGPG ; so adjust input pointers
PJRST FINDN ; Find line after new page mark
SUBTTL FIND -- Subroutine to find a desired line
; Call with DPG containing the desired page
; SINDEX containing the desired line
; Returns with T1=line found (either equal to SINDEX or next higher)
; PNTR points to desired line (or one higher)
FIND:: CKPTR.
PUSHJ P,SETRFL##
TRNE FL,SSBGSN ; Is CPG setup?
JRST FNDWRP ; No, go fix it
MOVE T1,CPG ; Get current page number
CAMN T1,DPG ; On right page?
JRST FNDLIN ; Yes, find the line
CAML T1,DPG ; Current page too small
JRST FINDPP ; No, must backup
SKIPN T1,(PNTR) ; Load current line number
JRST FIND3 ; This is last page and empty
;
FIND1: CKPTR.
CHKREN COMND## ; ^C^C then Re-enter?
CAMN T1,PGMK## ; Is this a page mark?
JRST FIND2 ; No, keep looking
PUSHJ P,FORLIN ; Advance over the page mark
JUMPN T1,FIND1 ; Keep looking for the page mark
JRST FIND3 ; End of file, return a zero, set BGPG
CONT.
; Here when a page mark was just found. Try to advance over it.
FIND2: PUSHJ P,FORLIN ; Advance to next line
AOS T2,CPG ; Now on next page
JUMPE T1,FIND3 ; Hit end of file, return zero
CAME T2,DPG ; Is it the one we're looking for?
JRST FIND1 ; No, keep looking
JRST FNDLIN ; Yes, find the line
FIND3: TRNE FL,SSBGSN ; Reached end without seeing BIGP?
POPJ P, ; Yes, don't set it then
TRO FL,BGSN ; Biggest page has been seen
MOVE T2,CPG
MOVEM T2,BGPG
POPJ P, ; Return zero for end of file
; Here when the current page number is larger than the desired page
; number. Back up to get the desired page.
FINDPP: CHKREN COMND## ; Have we re-entered?
PUSHJ P,BKPLIN ; Move backward one line
JRST FNDWRP ; Have to wraparound, not in buffer
CAME T1,PGMK## ; Find a page mark?
JRST FINDPP ; No, keep looking
SOS T2,CPG ; Yes, decrement count
CAME T2,DPG ; Is this the wanted page?
JRST FINDPP ; No, keep looking
;; PJRST FNDLIN ; Fall thru to find the line
CONT.
; Here when PNTR points to a line on the desired page. Position
; to find the appropriate line.
FNDLIN: CKPTR.
MOVE T1,(PNTR) ; Fetch current line number
CAME PNTR,BUFLAS ; End of buffer?
CAMN T1,PGMK ; or pointing to a page mark?
JRST FNDLN2 ; Yes, must move backward then
;
FNDLN1: CKPTR.
CHKREN COMND##
CAMG T1,SINDEX ; Too far forward?
JRST FNDLFF ; Yes, must move pointer forward
FNDLN2: PUSHJ P,BKPLIN ; Backup to previous line
JRST FNDWRP ; Must wrap, line not in buffer
CAME T1,PGMK ; Hit a page mark?
JRST FNDLN1 ; Try again
PUSHJ P,FORLIN ; Space over it then
;; PJRST FNDLFF ; And handle as forward space
; Here when the desired line is in the high part of the buffer
FNDLFF: CKPTR.
CHKREN COMND## ; Have we re-entered?
CAME T1,PGMK## ; Is this a pagemark?
CAML T1,SINDEX ; Is this the line?
POPJ P, ; Yes, return it
PUSHJ P,FORLIN ; No, get the next one
JUMPN T1,FNDLFF ; Look for another if not EOF
POPJ P, ; Return at end of file
; Here with BKPLIN gives a failure return. The line we want
; is in the part of the file that has already been written out.
FNDWRP::SETZM OLDLIN ; Avoid spurious sequence checks
TRNE FL,BOF ; Is the first part of the file in core
JRST FNDFST ; Yes, just return the first line
TLNN FL,COPFIL ; Are we copying?
TRNE FL,READOF ; Read only mode?
JRST FNDWRR ; That's special also
; Here on full edit. See if any changes have been made this pass.
TLNN FL,TECOF ; Finish copy if unsequenced
SKIPE CHGCNT## ; Any changes this pass?
JRST FNDWRF ; Yes, full wraparound needed
MOVE T1,OUTSIZ ; Get the last size
MOVE T2,PNTNMO ; Point to output block
CAML T1,XRBMWL(T2) ; This size greater
MOVEM T1,XRBMWL(T2) ; Yes, remember it
; Come here to rewind the input file. Either no changes last
; pass or file is being read in read only mode (join at FNDWRR).
FNDWRR: TLNN FL,TECOF ; Unsequenced mode?
JRST FNDWRS ; No
;
; Here to perform rewind if input file is unseqenced.
FNDRWT: PUSHJ P,IREADT ; Special first read function
SETZM OUTSIZ ; Clear output file size
JRST FIND ; Try to find the line again
; Here on full edit. Must finish writing the file
FNDWRF::PUSHJ P,OCOMPL ; Complete the output
PUSHJ P,XCHCHN ; Exchange channel assignments
SKIPE AUXFIL ; Are we reading a .TMP file
JRST FNDWRS ; Yes, don't have to make a one
; Here on full wraparound for the first time. Must open up the
; AUXIALIARY file.
MOVE T3,PNTNMO ; Point to input lookup block
MOVE T1,EDNAM ; Get name used for temporary files
HLLZ T2,XRBTFX(T3) ; Extension for next output file
DMOVEM T1,.RBNAM(T3) ; Establish as input file name
MOVE T1,PNTNMI ; Point to output block
MOVE T2,.RBPPN(T1) ; Get the PPN (path)
MOVEM T2,.RBPPN(T3) ; Save as next output Path
MOVE T2,.RBVER(T1) ; File version
MOVEM T2,.RBVER(T3) ; Store file version in .TMP file
MOVE T2,OUTSIZ ; Get number of words we wanted to write
ADDI T2,BLKSIZ-1 ; Round to next whole block
LSH T2,W2BLSH ; Convert to blocks
MOVEM T2,.RBEST(T3) ; Request this much from Monitor
SETZM .RBALC(T3) ; To save whole file
MOVE T2,.RBDEV(T1) ; Last output device
MOVEI T1,@CHNTAB+OUT ; Point to device block
MOVEM T2,.OPDEV(T1) ; Place in open block for IN channel
OPNDSK OUT,@CHNTAB+OUT ; Open up the channel
JRST CWTOF ; Can't write output file
PUSHJ P,MAKFIL## ; Make a new output file
JRST CWTOF ; Can't
IFN CRYPSW,<
MOVE T1,OCODE ;INPUT AND OUTPUT ENCRYPTION KEYS...
MOVEM T1,ICODE ; ARE NOW THE SAME
>
SETOM AUXFIL ; Flag auxilliary file now in use
; Here after wraparound to reset flags, etc.
FNDWRS: SETZB T1,OLDLIN ; Used to order check lines
EXCH T1,CHGCNT ; Fetch old value of change count
SKIPL T1 ; Did we just do a save?
ADDM T1,TOTCHG## ; No, increment total for all passes
PUSHJ P,IREAD ; Perform initial read
JRST FIND ; And try again to find the line
; Here if we can't start up the copy for some reason
; By this time, the UUO handler will have already typed the reason
; to the user.
CWTOF: CLRBFI ; Clear typeahead
OUTSTR [ASCIZ/?Can't write next temp file/]
SKIPN BAKF1 ; Have we renamed his original file?
JRST CWTOF1 ; No, ask him if he wants to exit
OUTSTR [ASCIZ/--Exiting.
/]
JRST CWTOF2
CWTOF1: OUTSTR [ASCIZ/. Save changes so far? /]
PUSHJ P,CONFRM## ; Ask user?
JRST CWTOF3 ; He said no
CWTOF2: MOVMS CXFMNE ; Allow first file exit
SETOM TELFLG## ; Force file name to be typed
MOVEI T1,12 ; Line feed will stop NSCAN
MOVEM T1,LIMBO ; Save it
PUSHJ P,NSCAN## ; Setup device, etc.
JFCL ; Should never happen
SKIPGE UNSEQF ; Dequence specified?
OUTSTR [ASCIZ/%Line numbers will be left on output file
/]
SETZM UNSEQF ; Indicate to SOSEND
JRST SWPXIT## ; And end this mess
CWTOF3: PUSHJ P,XCHCHN ; Change channels back
JRST QUIT##
; Here to return the first line
FNDFST: TRZ FL,SSBGSN ; Clear this so don't loop
IFN FTDEBUG,<
CAME LPNTR,BUFLOW ; Make sure low buffer is empty
NERROR ICN ; It isn't
>
MOVE T1,0(PNTR) ; Fetch first word
CAMN PNTR,BUFLAS ; Buffer empty?
SETZ T1, ; Return a zero
POPJ P, ; Else the line number
SUBTTL FORLIN -- subroutine to advance the line pointer forward
; Subroutine to advance the line pointer forward.
;
; Call with LPNTR and PNTR setup
; Returns with LPNTR and PNTR advanced and T1 containing the next line
; number. If T1 is zero, then end of file was reached and the high
; part of the buffer is empty.
FORLIN::CKPTR.
MOVEI T2,(PNTR) ; First word of high buffer
SUB T2,BUFLAS ; Negated number of words in high buffer
HRL PNTR,T2 ; Make AOBJN word
JUMPGE PNTR,FORLN3 ; If high buffer empty
MOVE T1,(PNTR) ; Fetch a word
JRST FORLN2 ; Skip test on first pass
;
FORLN1: MOVE T1,(PNTR) ; Fetch a word
TRNE T1,1 ; Line bit set?
JRST FORLN4 ; Yes, done
FORLN2: MOVEM T1,(LPNTR) ; Move word to low area
AOBJP LPNTR,FORLN5 ; Increment low pointer, check block end
FORLN6: AOBJN PNTR,FORLN1 ; And check next word
CONT.
; Here if the end of the buffer is reached
FORLN3: SETZ T1, ; Anticipate end of file
TRNE FL,EOF ; Check end of file
JRST FORLN4 ; Yes, return the zero
MOVE T2,HIPG ; Get desired position
CAME T2,CPG ; On the right page?
SKIPA T1,BUFHSZ ; Number of words to advance
MOVE T1,BUFSAI ; Only one block on page
HRRZS PNTR ; Clean up the pointer
PUSHJ P,ADVBFR ; Advance the buffer
CKPTR.
MOVEI T2,(PNTR) ; First word of high buffer
SUB T2,BUFLAS ; Negated number of words in high buffer
HRL PNTR,T2 ; Make AOBJN word
PUSHJ P,STLPNT ; Re-fix LPNTR
JUMPL PNTR,FORLN1 ; Look for next line
SETZ T1, ; Make sure T1 is correct
FORLN4: HRRZS PNTR ; Clean up the pointer
JUMPE T1,SETOLN ; If zero, don't check sequence
CAMN T1,PGMK ; Is this a page mark
PJRST CLROLN ; Reset old line number
CAMLE T1,OLDLIN ; How does this compare with last one
JRST SETOLN ; Just fine
RERROR ORDER
MOVE T2,CPG ; Oops, we have a sequence error. Page
PUSHJ P,PGPRN1## ; Should be typed for him
PUSHJ P,OUTLIN## ; As well as the offending line
MOVE T1,(PNTR) ; Restore the line number
PJRST SETOLN ; Setup OLDLIN
; Here to line block the low buffer when block boundary is crossed
FORLN5: PUSH P,PNTR ; Save PNTR
MOVEI PNTR,1(PNTR) ; Make it correct
PUSHJ P,BLKCHK ; Go fix low buffer
MOVEI T1,(PNTR) ; High
SUBI T1,(LPNTR) ; - Low
CAIL T1,BLKSIZ ; Plenty of room left?
JRST FORLN7
MOVEI T1,BLKSIZ ; Amount of space we need
PUSHJ P,DUMPBF ; Make some space
FORLN7: POP P,PNTR
PUSHJ P,STLPNT ; Setup LPNTR
JRST FORLN6 ; and join processing
;Subroutine to setup LPNTR with a minus count of words left to go
;till we reach the next block boundary. This must be done before
;entering FORLIN's main loop.
STLPNT: MOVEI T3,(LPNTR) ; Where we are now
TRZ T3,BLKSIZ-1 ; Clear low order bits
IOR T3,BUFBDY ; Guess next block boundary
CAIG T3,(LPNTR) ; Right?
ADDI T3,BLKSIZ ; No, got the previous one. Fix it
SUBI T3,(LPNTR) ; Length in words till next block
MOVNI T3,(T3) ; Negate the count
HRLI LPNTR,(T3) ; Fix up LPNTR
POPJ P, ; and return
SUBTTL BKPLIN -- Subroutine to backup one line in the buffer
; Call with PNTR and LPNTR setup.
; PUSHJ P,BKPLIN
; <reached beginning of buffer>
; <PNTR & LPNTR setup, T1=(PNTR)>
BKPLIN::CKPTR.
MOVEI T2,(LPNTR) ; Point to low buffer limit
SUB T2,BUFFIR ; Subtract first word giving amount
JUMPLE T2,CPOPJ## ; If low part empty
;
BKPLN1: SUB LPNTR,[1,,1] ; Point to highest word in low buffer
SKIPN T1,(LPNTR) ; Fetch it
JRST BKPLN2 ; If zero word, ignore it
SUBI PNTR,1 ; and adjust PNTR also
MOVEM T1,(PNTR) ; Move to high part
TRNE T1,1 ; Line number bit set?
JRST BKPLN3
BKPLN2: SOJG T2,BKPLN1 ; No, move next word if there is one
PUSHJ P,FXLPNT ; Fix the pointer
PJRST CLROLN ; Clear OLDLIN and return
BKPLN3: MOVEM T1,OLDLIN
PUSHJ P,FXLPNT ; Fix the pointer
JRST CPOPJ1## ; Give good return
;CLROLN -- Routine to Clear OLDLIN after backing up or crossing page
; boundary
;Call
; PUSHJ P,CLROLN
; <return here>
CLROLN::SETZM OLDLIN ; Clear OLDLIN
POPJ P, ; and return
; FXLPNT -- Routine to fix up LPNTR to get block count in range
;
; Call
; PUSHJ P,FXLPNT
; <return here>
FXLPNT: MOVE T3,LPNTR ; Copy the pointer
FXLPN1: ADD T3,[BLKSIZ,,0] ; Try fixing it
JUMPGE T3,CPOPJ ; Better?
MOVE LPNTR,T3 ; Yes, try again
JRST FXLPN1 ; Until best
SUBTTL ADVBFR -- Subroutine to advance the buffer
; ADVBFR -- Subroutine to advance the buffer
;
; Call with T1 = number of words to advance with .T1 MOD 128 EQL 0
ADVBFR::CKPTR.
MOVEI T2,(PNTR) ; Pointer to high buffer
SUBI T2,(LPNTR) ; Amount of free space left
CAIL T2,BLKSIZ(T1) ; Room for requested input
PJRST DATAIN ; Go read the data
PUSH P,T1 ; Save advance request
SUB T1,T2 ; How much space we are short
ADDI T1,2*BLKSIZ-1 ; Round into next block size
TRZ T1,BLKSIZ-1 ; Then to even blocks
PUSHJ P,DUMPBF ; Dump to make room
POP P,T1 ; Restore request
PJRST DATAIN ; Read the data and return
SUBTTL INPUT routines for SOS
; IREAD -- Subroutine to perform the initial read
;
IREAD:: PUSHJ P,IPNTRS ; Initialize pointers
USETI IN,1 ; Rewind the input file
SETOM IBUF+3 ; Zero block for decryption
IREAD0: MOVE T1,BUFHSZ ; Size of half the buffer
PUSHJ P,DATAIN ; Fill the buffer
CAMN PNTR,BUFLAS ; Nothing read at all? (Null file)
TRNE FL,EOF ; And not end of file
CAIA
JRST IREAD0 ; Yes, get next block (all zeros read)
MOVE T1,(PNTR) ; Look at first word in buffer
CAME PNTR,BUFLAS ; Nothing read? Assume numbered
TRNE T1,1 ; Is it a line number
POPJ P, ; Yes, all done. File is numbered
; Here if the file is not line numbered
TLOE FL,TECOF ; Set TECO mode on input
ERROR ICN ; Already set?
SKIPN UNSQIF ; Do we already think we know?
SKIPE BASICF ; or BASIC mode?
CAIA ;
SETOM UNSQIF ; No, flag this as TECO input file
SKIPGE XFLAG ; Did he say /X
TLZ FL2,LNUMF ; Yes, suppress line numbers
SETSTS IN,.IOASL ; Change mode to ASCII LINE
MOVE T2,[%LDNDB] ; Default number of buffers
GETTAB T2, ; Read it
MOVEI T2,2 ; Assume 2
SKIPN T1,BUFNUM ; Number to be used
MOVE T1,T2 ; Else default
IMULI T1,BLKSIZ+3 ; Amount of space required
ADD T1,.JBFF ; Amount of core required
CORE T1, ; Get it now (if INBUF can't get
ERROR NEC ; he gets an address check)
HRLZ T3,SVJFF2 ; Nominal core size now
INBUF IN,@BUFNUM## ; Allocate input buffers
HRR T3,.JBFF## ; Nominal core size with RT buffer ring
HRRZM T3,SVJFF2 ; Save for SOSCMD
HRRZM T3,CXFCOR ; Save here also
MOVEM T3,RDTBFP ; Remember the address range of buffers
MOVE T1,CHNTAB+IN ; Point to IN open block
HRRZ T1,.OPBUF(T1) ; Point to buffer header
MOVSI T2,(POINT 7,) ; 7 bit byte pointer
MOVEM T2,.BFPTR(T1) ; Pass on to monitor
SETZM .BFCTR(T1) ; Clear word count, just in case
; Enter here to setup initial read if TECO mode is already selected.
; This initialize the co-routine and starts the initial read
IREADT: MOVE T2,RTIPDP ; Fetch Read Teco PDL
PUSH T2,RDTREF ; Save the initial PC
MOVEM T2,RDTPDP ; Restore for later
SETZM IBUF+.BFCTR ; Make sure we start at the top
MOVEI T1,@CHNTAB+IN ; To the open block
HRRZ T1,.OPBUF(T1) ; To input buffer header
USETI IN,-1 ; Set end of file
IREAD1: SKIPL @.BFADR(T1) ; Use bit set here?
JRST IREAD2 ; No, finished flushing
INPUT IN, ; Advance to next buffer
JRST IREAD1 ; Until done
IREAD2: PUSHJ P,INIPAG ; Initialize for a new page
JRST IREAD ; Go do the initial read
; Subroutine to initilize the buffer pointers. Called from IREAD
; and by CREATE code in SOSINT.
IPNTRS::MOVEI T1,1
MOVEM T1,CPG ; Initilize current page
TRO FL,BOF ; Note at beginning of file
TRZ FL,EOF!SSBGSN ; and not at end
TRNN FL,READOF ; Read only?
USETO OUT,1 ; If not, make sure we write from top
MOVE T2,PNTNMI ; Point to input name
MOVE T2,.RBSIZ(T2) ; Then to size of the input file
MOVEM T2,REMSIZ ; No, save input file size as REMSIZ
SETZM OUTSIZ ; Zero size of output file written
SETOM OBUF+3 ; Initialize block number for encryption
MOVE PNTR,BUFLIM ; Initialize high buffer pointer
MOVEM PNTR,BUFLAS ; And note it is empty
MOVE LPNTR,BUFLOW ; Initialize low buffer pointer
POPJ P, ; and return
SUBTTL DATAIN -- Subroutine to read data into the buffer
;
; Call with T1=number of words to read. Should normally be a multiple
; of 128 words.
DATAIN: CKPTR.
TRNE FL,EOF ; At end of file?
POPJ P, ; Yes, just return
MOVNS T2,T1 ; Number of words to read
ADD T2,BUFLIM ; Compute first input word
HRLZS T1
HRRI T1,-1(T2) ; Address part of IOWD
MOVEI T2,(PNTR) ; Point to first word in high buffer
SUB T2,BUFLAS ; Compute number of words in high buffer
JUMPE T2,DATAN1 ; If no compression needed
ADDI T2,1(T1) ; Where to move first word
HRLI T2,(PNTR) ; Where first word is now
HRRI PNTR,(T2) ; Update PNTR
BLT T2,(T1) ; Move current data down
JRST DATAN2
DATAN1: MOVEI PNTR,1(T1) ; Fixup pntr
DATAN2: TLNE FL,TECOF ; Unsequenced input?
JRST DATAN4 ; Yes
HLRE T3,T1 ; Count of words to read
ADDB T3,REMSIZ ; Update amount of file remaining
SETZ T2, ; Set stop word
INUUO IN,T1 ; Read using T1 as IOWD
JRST DATAN3 ; If read successfully
STATZ IN,IO.ERR ; Skip if just end of file check
ERROR DIE ; Fatal input error
DATAN3:
IFN CRYPSW,<
PUSHJ P,CRPIOI ; Yes, go do it
>
SETZ T1, ; Assume no shortfall
JUMPG T3,DATAN5 ; Right?
MOVM T1,T3 ; Get count of shortfall
SETZM REMSIZ ; Note nothing left
TRO FL,EOF ; Note end of file
CONT.
; Join here to fixup pointers, zero compress the buffer and return
DATAN5: SUB T1,BUFLIM ; Subtract limit from shortfall
MOVMS T1
MOVE T2,BUFLIM ; Limit
MOVEM T2,BUFLAS ; Update BUFLAS
DATAN6: SKIPN T3,-1(T1) ; First word of data
JRST DATAN7 ; If this one to be skipped
MOVEM T3,-1(T2) ; Save if non-zero
SUBI T2,1 ; Decrease input pointer
DATAN7: CAILE T1,1(PNTR) ; Did we move the last word?
SOJA T1,DATAN6 ; No, do the next one
MOVEI PNTR,(T2) ; Reset PNTR
PJRST SETBLL ; Setup last line terminator in buffer
; Here if unsequenced -- zero compression is not needed here since
; it has already been performed.
DATAN4: PUSH P,C ; Save C
PUSHJ P,RDTECO ; Read in unsequenced data
POP P,C ; Restore C
SUB T1,BUFLIM ; Subtract limit from shortfall
MOVMS T1
MOVEM T1,BUFLAS ; Update BUFLAS
SETZM (T1) ; Make sure zero word follows buffer
; Here to set the value of BUFBLL, which is the last
; word in the buffer with a line number bit set. If the value
; of PNTR is less than BUFBLL, then it is not necessary to call
; GETLTH to ensure that a line is in core.
SETBLL: TRNE FL,EOF ; End of file
JRST SETBL2 ; Yes, all lines in core
MOVE T1,BUFLIM ; LWA+1 of buffer
SUBI T1,1 ; Point to last word in buffer
MOVEI T2,1 ; Line terminator
SETBL1: TDNE T2,(T1) ; Check for line terminator
JRST SETBL3 ; Yes have one
CAILE T1,1(PNTR) ; Have we reached the end
SOJA T1,SETBL1 ; No, keep looking
SETZM BUFBLL ; None, set small value
POPJ P,
SETBL2: MOVE T1,BUFLIM ; Use a suitably high value
SETBL3: MOVEM T1,BUFBLL ; Got one, set it up
POPJ P,
SUBTTL Subroutine to perform unsequenced input
RDTECO::PUSHJ P,SAVR## ; Save T3,T4 and T5
HRRI T4,1(T1) ; Address of first word to read
HRLI T4,(POINT 7,) ; Form byte pointer to it
HLRE T5,T1 ; Get count of words to get
MOVMS T5 ; Make positive
IMULI T5,5 ; Convert to byte count
SETZM 1(T1) ; Clear a word
AOBJN T1,.-1 ; and all others in IOWD range
JRST RESUME
; Here when everything is normal
RDTEC1: SOSG IBUF+.BFCTR ; See if anything in data buffer
PUSHJ P,RDTGET
ILDB C,IBUF+.BFPTR ; Fetch a charcter
CAIG C,15 ; Special character?
JRST RDTEC3 ; Yes, go handle
RDTEC4: TLZE FL,L1.NNL ; Need new line?
PUSHJ P,RTNLN ; Yes, make one
IDPB C,T4 ; Store character in buffer
SOJG T5,RDTEC1 ; Loop for more data
PUSHJ P,RDTXJR ; Return to main program
RDTREF: JRST RDTEC1 ; Get some more data
; Here on a special character
RDTEC3: CAIN C,14 ; Is it a formfeed?
JRST RDTFF ; Yes,
CAIN C,12 ; Is it a linefeed?
JRST RDTLF ; Yes, go handle
JUMPE C,RDTEC1 ; Is it null, ignore it
SKIPN SWUNDER## ;WANT UNDERLINE?
CAIE C,15 ;OR NOT A <CR>
JRST RDTEC4 ;YES--JUST OUTPUT IT
JRST RDTEC1 ;NO--IGNORE IT
; Here on a bare form feed
RDTFF:
PUSHJ P,RTPGMK ; Then a page mark
PJRST RDNLC ; Advance to next line
; Here on a line feed
RDTLF: PUSHJ P,RTCRLF ; Output a CRLF
;; PJRST RDNLC
; Here to advance to the next word and output a sequence number
RDNLC: TLO FL,L1.NNL ; Note we need a new line
JRST RDTEC1 ; And continue processing
; Here to insert a CRLF
RTCRLF::MOVEI C,15 ; CR
PUSHJ P,RTIOC ; Send it out
MOVEI C,12 ; LF
PUSHJ P,RTIOC ; Send it out too
PJRST RTALGN ; And align to next word
; Here to insert a page mark
RTPGMK: PUSHJ P,RTALGN ; Align to next word
MOVE T1,PGMK ; Fetch a pagemark
PUSHJ P,RTIOW ; Put word in the buffer
MOVE T1,PGMKW2 ; Second word
PUSHJ P,RTIOW ; Second word
;; PJRST INIPAG ; Initialize for a new page
; Subroutine to reset TECLIN and set L1.NNL. Call at start of
; a new page or start of file
INIPAG: TLO FL,L1.NNL ; Note that we need a new line
SETZM TECLIN ; Set for initial line number
POPJ P, ; Return
; Here to start a new line
; This routine preserves C
RTNLN: PUSH P,C ; Save next input character
PUSHJ P,RTALGN ; Zero fill to a word boundary
CAIGE T5,5 ; Room for the line number?
PUSHJ P,RDTXJR ; No, pause then
SKIPN BASICF ; Is this a basic mode file?
JRST RTNLN1 ; No, generate a new sequence number
RDBAS1: POP P,C ; Get back next character
CAIE C," " ; Skip spaces
CAIN C,11 ; and tabs
JRST RDBAS1 ; Loop until not space or tab
CAIG C,"9" ; Is it
CAIGE C,"0" ; a digit
ERROR BBF ; First must be a digit
MOVE T1,C ; Initialize accumulator
RDBAS3: PUSHJ P,RTCHAR ; Get next character
CAIG C,"9" ; Is it
CAIGE C,"0" ; a digit
JRST RDBAS2 ; If no, end of sequence number
LSH T1,7 ; Advance accumulator
IOR T1,C ; OR in this digit
JRST RDBAS3 ; And look for next
RDBAS2: CAIE C," " ; Is this a blank
CAIN C,11 ; or tab?
PUSHJ P,RTCHAR ; Yes, get something else
PUSH P,C ; Save next character
LSH T1,1 ; Make space for sequence bit
IOR T1,LNZERO## ; Supply zero fill and seq bit
MOVEM T1,(T4) ; Plant the number in the buffer
JRST RTNLN4 ; and join processing
RTNLN1: SKIPN T1,TECLIN ; Save as this line number
JRST RTNLN2 ; If not initialized
CAML T1,TECINC ; Smaller than increment?
JRST RTNLN3 ; Yes, we must have wrapped around
MOVNI T1,2 ;GET -2 (EXIT CONTIGIOUS)
CAME T1,.UNSQF ;SET?
CAMN T1,UNSEQF ;..
CAIA ;YES--FORGET MESSAGE
RERROR PMI ;NO-- TELL HIM
SETOM PMIFLG ;FLAG WE ADDED ONE
PUSHJ P,RTPGMK ; Install a page mark
TLZ FL,L1.NNL ; Clear need new line flag
CAIGE T5,5 ; Check for room
PUSHJ P,RDTXJR ; Wait
JRST RTNLN1 ; And try again
RTNLN2: MOVE T1,TECFST ; Get starting line number
MOVEM T1,TECLIN ; Save for next time
RTNLN3: MOVEM T1,(T4) ; Save in the buffer
MOVE T2,TECINC ; Get increment
PUSHJ P,ASCIAD## ; Add to get next line number
MOVEM T1,TECLIN ; Save for next time
RTNLN4: ADDI T4,1 ; Increment the pointer again
SUBI T5,5 ; And decrement remaining data count
CAIGE T5,1 ; See if room left for the tab
PUSHJ P,RDTXJR ; No, pause
MOVEI C,11 ; Get a tab
PUSHJ P,RTIOC ; Output it and return
JRST CHPOPJ## ; Restore next character and return
; Subroutine to align the output pointer on the next word boundary
; Call with
; PUSHJ P,RTALGN
RTALGN: MOVEI T1,(T5) ; Count
IDIVI T1,5 ; Compute remaining count
SUBI T5,(T2) ; Decrement by that many
JUMPL T4,CPOPJ ; If byte pointer is already aligned
HRLI T4,(POINT 7,) ; Point to pre-first byte
ADDI T4,1
POPJ P,
; Subroutine to emit a word. Call with output word aligned
; by calling RTALGN first. This routine leaves the output
; word aligned if sucessive calls are needed.
RTIOW: CAIGE T5,5 ; Room for it
PUSHJ P,RDTXJR ; No, resume calling process
MOVEM T1,(T4) ; Store the page mark
SUBI T5,5 ; Decrement the counter
AOJA T4,CPOPJ ; Increment pointer 1 word and return
; Subroutine to emit a single character
RTIOC: TLZE FL,L1.NNL ; New line waiting?
PUSHJ P,RTNLN ; Yes, send it out
IDPB C,T4 ; Send out the character
SOJG T5,CPOPJ ; If room for still more
;; PJRST RDTXJR ; Else return to caller of RDTECO
; Here to resume the calling process
RDTXJR: PUSH P,T1 ; Save T1
SETZ T1, ; Clear it
PUSHJ P,RESUME ; Resume caller
POP P,T1 ; Restore T1
POPJ P, ; And restart the input
; Routine to get a single character from an unsequenced input file
RTCHAR: SOSGE IBUF+.BFCTR ; Check buffer count
PUSHJ P,RDTGET ; Refill buffer
ILDB C,IBUF+.BFPTR ; Get next character
JUMPE C,RTCHAR ; Ignore nulls
POPJ P, ; Return it
; Here to get data for TECO mode input
RDTGET: INUUO IN, ; Do buffer mode input
PJRST SIBPWC ; Setup BPWC
STATZ IN,IO.ERR ; Check for errors on input
ERROR DIE ; Yes, die
STATO IN,IO.EOF ; Skip if end of file
ERROR ICN ; What?
; here on end of file
MOVEI T1,(T5) ; Get count remaining
IDIVI T1,5 ; Compute shortfall
SETZ C, ; Clear C
JUMPE T2,RDTGT1 ; If remainder of word is null
IDPB C,T4 ; Clear remainder of last word
SOJG T2,.-1 ; Loop till end of word
RDTGT1: TRO FL,EOF ; Note that we've reached end of file
;; PJRST RESUME ; Return to caller of RDTECO
; Here to resume caller of RDTECO
RESUME: EXCH P,RDTPDP ; Get back callers push down list
POPJ P, ; And return to him
SUBTTL Subroutine to compute the length of the current line
; GETLTH -- Subroutine to return the length of the current line
;
; Call with PNTR set up
; Return T1=length unless no current line, then T1=0
GETLTH::CKPTR.
PUSH P,PNTR ; Save PNTR
MOVEI T1,(PNTR) ; Point to first word in high buffer
SUB T1,BUFLAS ; Compute number of words in high buffer
HRLI PNTR,(T1) ; Make AOBJN pointer
AOBJP PNTR,GETLT3 ; Skip if 1 word or less
MOVEI T2,1 ; Bit for testing
;
GETLT1: TDNE T2,(PNTR) ; See if line bit set
JRST GETLT4 ; Yes, done
AOBJN PNTR,GETLT1 ; Keep looking
;
; Here if hit end of buffer before end of line
GETLT2: MOVEI T1,(PNTR) ; Current end
SUB T1,(P) ; Compute length so far
CAILE T1,BLKSIZ ; Excessively long?
ERROR ELL ; Yes, die now
GETLT3: TRNE FL,EOF ; End of file in core?
JRST GETLT4 ; Yes, that's the end then
HRRZS PNTR ; Clear counter in left half
SUB PNTR,(P) ; Compute length so far
EXCH PNTR,(P) ; Get old value of PNTR
MOVE T2,HIPG ; Highest page in command
CAMLE T2,CPG ; Long way off?
SKIPA T1,BUFHSZ ; Yes, read lots of data
MOVE T1,BUFSAI ; No, just read in a little
PUSHJ P,ADVBFR ; From the file
POP P,T1 ; Restore old count to T1
CAMN PNTR,BUFLAS
POPJ P, ; Return if no more data
JRST GETLTH ; Try this again
; Here when end of line is found
GETLT4: MOVEI T1,(PNTR) ; Get new end
SUB T1,(P) ; Compute displacement
POP P,PNTR ; Restore old pointer
POPJ P,
SUBTTL BLKCHK -- Routine to check for line blocking
; This routine is called whenever LPNTR is moved across a block
; boundary to ensure that each block in the low buffer is properly
; line blocked. Normally when this condition is reached, a
; partial line will exist at the top of the low buffer. This
; routine detects this condition and moves this partial line out
; of the last block, then zeros the remainder of the block. When
; it returns, LPNTR is properly setup, and all completed blocks in
; the low buffer are properly line blocks and ready for output.
;
; Proper functioning of this routine requires that there always be
; enough free space between the high and low buffers so that the
; line can always be moved. No checking is done here. A
; sufficient condition to ensure that this is the case is to
; always leave one block of free space in the buffer. Space can
; always be made by calling the routine DUMPBF whenever
; insufficient space remains in the buffer. The amount of free
; space left should always be checked whenever data is added to
; the buffer. Since this routine can use up some of the free
; space, a check should be made after this routine is called.
CONT.
BLKCHK:
IFN FTDEBUG,<
MOVE T3,LPNTR ;Low pointer
TRZ T3,777600 ;All RH bits except low 7
CAME T3,BUFBDY ;Make sure we're really there
ERROR ICN ;Nope, bum call
>
MOVEI T3,1 ; Line sequence bit for testing
CAME PNTR,BUFLAS ; Or end the buffer
TDNE T3,(PNTR) ; Does next word start a line?
POPJ P, ; Yes, then we are line blocked
;
;Here to find the start of the partial line
;
PUSH P,T4 ; Save T4
MOVEI T4,-1(LPNTR) ; Last word of partial line
TDNN T3,(T4) ; This one started it?
SOJA T4,.-1 ; No, backup up and look again
;
;Here with T4 pointing to the partial line
;
MOVEI T3,(LPNTR) ; First free word after block
SUBI T3,(T4) ; Length of the line
CAILE T3,BLKSIZ ; Is it reasonable?
ERROR ELL ; Excessively long input line
PUSH P,T4 ; Save address of line
HRLS T4,(P) ; Copy address to both halves
HRRI T4,(LPNTR) ; Where to move the line to
ADDI LPNTR,(T3) ; Update LPNTR
BLT T4,-1(LPNTR) ; Shift partial line out of block
POP P,T4 ; Restore pointer to line
ADDI T4,1 ; Make it [addr,,addr+1]
ADDI T3,-1(T4) ; Point to old LPNTR
SETZM -1(T4) ; Clear first word
CAILE T3,(T4)
BLT T4,-1(T3) ; Then the rest of the line
CKPTR.
POP P,T4 ; Restore T4
POPJ P,
SUBTTL LOADCL -- Subroutine to load the current line into LIBUF
; LOADCL -- Subroutine to load the current line into LIBUF
;
; Call with PNTR setup. Returns CPOPJ
; On return, T1 contains the length of the line.
LOADCL::CKPTR.
MOVE T1,[LIBUF,,LIBUF+1]
SETZM LIBUF
BLT T1,LIBUF+MXWPL+1
PUSHJ P,GETLTH ; Get length of this line
JUMPE T1,CPOPJ## ; Just return if no words
CAILE T1,MXWPL ; Make sure not too long
NERROR LTL ; Oops, line is too long
HRRI T2,LIBUF ; Destination
HRLI T2,(PNTR) ; Source
BLT T2,LIBUF-1(T1) ; Transfer it
MOVE T2,[POINT 7,LIBUF,6] ; Point to the line buffer
LOADC1: ILDB C,T2 ; Look at next character
SKIPN 0(T2) ; End of line?
NERROR ILFMT ; Must end with CRLF
CAIE C,15 ; Found the carriage return yet?
JRST LOADC1 ; No keep looking
ILDB C,T2 ; Yes, get next
CAIE C,12 ; Next must be linefeed
NERROR ILFMT ; Nope, don't take it
ILDB C,T2 ; Get the next one
JUMPE C,CPOPJ## ; Must be the end now
NERROR ILFMT ; Else that is no good either
SUBTTL OCOMPL -- Subroutine to finish out a file
; OCOMP1 -- Subroutine to finish out a file
;
OCOMPL::PUSHJ P,OCOMP1 ; Finish writing output file
PJRST REWOUP ; Rewind so next pass can read it
; Here to comple writing the output file
OCOMP1::CKPTR.
TRO FL,SSBGSN ; We won't count pages here
MOVEI T3,(PNTR)
SUBI T3,(LPNTR) ; Free space
CAIGE T3,BLKSIZ ; Enough to try again?
JRST OCOMP2 ; No, dump some first
HRLI T1,(PNTR) ; Start of high buffer
HRRI T1,(LPNTR) ; First word of free space
MOVE T2,BUFLAS ; Limit of high buffer
SUBI T2,(PNTR) ; Size of high buffer
JUMPE T2,OCOMP2 ; No, go get more data
PUSHJ P,STLPNT ; Setup LPNTR and T3
MOVMS T3 ; Get magnitude
CAILE T2,(T3) ; Use smaller of high size or block size
MOVEI T2,(T3) ; from current low pointer position
HRLI T2,(T2) ; Both sides now
ADD LPNTR,T2 ; Adjust LPNTR (both halves)
ADDI PNTR,(T2) ; and PNTR (right half)
BLT T1,-1(LPNTR) ; Compress the buffer into low part
TLZN LPNTR,-1 ; On even boundary?
PUSHJ P,BLKCHK ; Ensure low buffer is line blocked
JRST OCOMP1
OCOMP2: MOVEI T1,(LPNTR) ; Point to limit of low part
SUB T1,BUFFIR ; Compute amount of data in buffer
CAMN PNTR,BUFLAS ; At end of buffer?
TRNN FL,EOF ; and end of file on last read
CAIA
PJRST DUMPBF ; Dump remainder
TRZ T1,BLKSIZ-1 ; Round down to whole blocks
PUSHJ P,DUMPBF ; And dump it out onto the file
MOVE T1,PNTR ; Point to first free word
SUBI T1,BLKSIZ(LPNTR); Free space minus 1 block
TRZ T1,BLKSIZ-1 ; Round down to whole blocks
JUMPLE T1,OCOMP1 ; If we can't take any more data
PUSHJ P,DATAIN ; And fill it (if possible)
JRST OCOMP1 ; Go finish out the file
SUBTTL DUMPBF -- Subroutine to dump the buffer
; DUMPBF -- Call with T1 = number of words to be dumped
; Returns CPOPJ
; DMPBFS -- Same call as DUMPBF, but gives non-skip return if
; no data is dumped.
; This routine does not affect either PNTR or the high buffer in any
; way. FORLIN depends on this fact.
DUMPBF: PUSHJ P,DMPBFS ; Call DMPBFS
POPJ P, ; Non-skip
POPJ P, ; and skip are the same
DMPBFS: CKPTR.
PUSHJ P,SAVR## ; Preserve T3-T5
MOVEI T2,(LPNTR) ; Limit of low buffer
SUB T2,BUFFIR ; Compute count in low buffer
CAMG T2,T1 ; Less than request?
MOVE T1,T2 ; Yes, use actual data count
MOVN T3,T1 ; Data count, negated
HRLZS T3 ; Move to left half
HRR T3,BUFFIR ; Make IOWD
SOJGE T3,CPOPJ ; Adjust the IOWD, check for zero
SETZ T4, ; Zero terminate IOWD list
IFN CRYPSW,<
PUSHJ P,CRPIOO ; Encrypt if needed
>
TRNN FL,READOF ; Skip OUTPUT if read only
OUTUUO OUT,T3 ; Output the data
JRST DMPBF1 ; No errors, keep going
ERROR DDE ; Error on output
DMPBF1: TRZ FL,BOF ; No longer at beginning of file
ADDM T1,OUTSIZ ; Increment amount written
HRLI T1,(T1) ; Both sides now
SUB LPNTR,T1 ; Update LPNTR
PUSHJ P,FXLPNT ; Fix pointer if needed
ADD T1,BUFFIR ; Compute address of first data word
MOVSS T1 ; Source goes in left half for BLT
HRR T1,BUFFIR ; Destination of the data
CAME LPNTR,BUFLOW ; Don't BLT if nothing to move
BLT T1,-1(LPNTR) ; Compress the buffer
JRST CPOPJ1## ; Give good return
; Subroutine to exchange input and output channel pointers.
; Also changes lookup/enter block pointers for reference by
; end code
XCHCHN::MOVE T1,CHNTAB##+IN ; Get IN channel entry
EXCH T1,CHNTAB##+OUT ; Exchange with OUT entry
MOVEM T1,CHNTAB##+IN ; Exchange with IN entry
MOVE T1,PNTNMI ; Then do lookup block pointers
EXCH T1,PNTNMO
MOVEM T1,PNTNMI
POPJ P,
; Subroutine to rewind the output file so it can perform input
; Note: This is a logical rewind only, no USETI is performed here.
; (A USETI IN,1 is done by IREAD instead)
REWOUP::PUSHJ P,CLRTEC ; Clear TECO mode if set
MOVEI T1,@PNTNMO ; Point to the output lookup block
AOSLE XRBNOR(T1) ; See if must open for update now
JRST REWOP1 ; Yes, don't re-open
PUSHJ P,MAKFL1## ; Yes, go open it
JRST CWTOF ; Can't write next temp file
REWOP1: MOVEI T2,@PNTNMO ; Point to its lookup block
MOVE T1,OUTSIZ ; Get the output size
MOVEM T1,.RBSIZ(T2) ; Save as input size for reading
CAML T1,XRBMWL(T2) ; See if this pass longer
MOVEM T1,XRBMWL(T2) ; Yes, update maximum written length
POPJ P, ; Return
; Subroutine to clear the TECOF flag
; Call with CHNTAB+IN pointing to the unsequenced input file.
CLRTEC::TLZN FL,TECOF ; See if input file was unsequence
POPJ P, ; Nothing else to do
MOVE T3,CHNTAB+OUT ; Point to input file channel block
SETSTS OUT,.IODMP ; Back to dump mode input
HRRZ T2,RDTBFP ; Read Teco buffer pointer
MOVSI T1,400000 ; Virgin buffer ring bit
MOVE T2,.OPBUF(T3) ; Point to buffer header
MOVEM T1,(T2) ; Save in header block
HLRZ T1,RDTBFP ; Point to first word of old buffer
SETJF2::MOVE T2,CXFCOR ; End of this segment
CAML T2,SVJFF2 ; Skip if not highest segment
MOVEM T1,SVJFF2 ; Let SOSCMD core back to there
MOVEM T1,CXFCOR ; Reduce length of this segment
POPJ P,
SUBTTL INSED -- Routines to insert and delete lines
; INSED -- Subroutine to insert and delete data words
; Call with
; NCNT = number of words to add from LIBUF
; OCNT = number of words to delete from buffer
; Returns CPOPJ
INSED:: SKIPE T1,OCNT ; Get count to delete
PUSHJ P,DLTLIN ; Delete that many words
SKIPN T1,NCNT ; Get count to insert
POPJ P, ; If nothing to insert
;; PJRST INSLIN ; and insert them from LIBUF
; Subroutine to insert data in the buffer
; Call with PNTR setup, T1 containing the number of words to insert
; Data should be in LIBUF. Uses T1-T5 (calls FIND)
; Return with Line number in T1.
INSLIN::CKPTR.
MOVE T2,PNTR ; Get high pointer
SUBI T2,(LPNTR) ; Get size of free space
CAILE T2,BLKSIZ(T1) ; Skip if free space too small
JRST INSLN1 ; Enough, continue
PUSH P,T1 ; Save space
MOVEI T1,BLKSIZ ; Size of one block
PUSH P,CPG ; Save current page
SKIPE T2,(PNTR) ; Current line number
CAMN T2,PGMK ; A page mark?
MOVE T2,LNOVER## ; Last possible line + 1
PUSH P,T2 ; Save to refind
PUSHJ P,DMPBFS ; Dump a block
TRO FL,SSBGSN ; Made no room, force a wraparound
POP P,SINDEX ; Restore line
POP P,DPG ; And page
PUSHJ P,FIND ; Find it again
POP P,T1 ; Restore the count
JRST INSLIN ; Go see if we need to dump a block
INSLN1: HRLI T2,LIBUF ; Point to line buffer
SUBI PNTR,(T1) ; Update PNTR to account for new line
HRRI T2,(PNTR) ; Point to destination
ADDI T1,-1(PNTR) ; Setup pointer to last word of line
BLT T2,(T1) ; Copy the date into the main buffer
AOS CHGCNT ; Count as a change
MOVE T1,LIBUF ; Get the line number
CAMN T1,PGMK ; Page mark?
PJRST CLROLN ; Yes, clear OLDLIN
SETOLN: MOVEM T1,OLDLIN ; Else set it up
POPJ P, ; Done
SUBTTL DLTLIN -- Subroutine to remove a line from the buffer
; Call with length in T1, PNTR setup
DLTPAG::MOVEI T1,2 ; Page mark is two words long
;
DLTLIN::CKPTR.
AOS CHGCNT ; Count as a change
MOVE T2,BUFLAS ; Last word of data
SUBI T2,(PNTR) ; Count of words in the buffer
CAML T2,T1 ; Is the requested amount present
JRST DLTLN1 ; Yes, continue
TRNE FL,EOF ; Are we at end of file?
ERROR ICN ; Yes, then we are very confused
PUSH P,T1 ; Save requested count
MOVEI T1,BLKSIZ ; Advance forward one buffer
PUSHJ P,ADVBFR ; Go advance the buffer
POP P,T1 ; Restore the count
JRST DLTLIN ; And try again
; Here when it is known that the whole line is in core
DLTLN1: ADDI PNTR,(T1) ; Increment PNTR
POPJ P, ; and return
SUBTTL SxBPWC -- Routines to setup byte pointer/word counts
; These routines setup the byte pointer and word count in IBUF
; (IN channel) or OBUF (OUT channel) from the channel specific
; buffer header blocks. They preserve all AC's.
SIBPWC::
IFN CRYPSW,<
PUSHJ P,CRPBFI ; Decrypt if necessary
>
PUSHJ P,SAVR## ; Save all temporaries used
MOVE T3,CHNTAB+IN ; Point to IN open block
HRRZ T3,.OPBUF(T3) ; Point to buffer header
DMOVE T4,.BFPTR(T3) ; Fetch Byte pointer and word cound
DMOVEM T4,IBUF+.BFPTR ; Place in IBUF
SETZM .BFCTR(T3) ; Prevent confusion
POPJ P,
; Here to setup OBUF for OUT channel
SOBPWC::PUSHJ P,SAVR## ; Save all temporaries used
MOVE T3,CHNTAB+OUT ; Point to OUT open block
HLRZ T3,.OPBUF(T3) ; Point to buffer header
DMOVE T4,.BFPTR(T3) ; Fetch Byte pointer and word cound
DMOVEM T4,OBUF+.BFPTR ; Place OUT OBUF
SETZM .BFCTR(T3) ; Prevent confusion
POPJ P,
SUBTTL -- Encryption support
IFN CRYPSW,<
;Routine to save AC's for the encryption routine
HAC==12 ; Highest AC to save
SAVCRX::ADJSP P,HAC+1 ; Need HAC+1 places on the PDL
MOVEM HAC,0(P) ; Save high AC first
MOVEI HAC,-HAC(P) ; Where to BLT to
BLT HAC,-1(P) ; Save the AC's
MOVE HAC,0(P) ; Restore in case needed again
PUSHJ P,@-<HAC+1>(P) ; Return to caller
MOVSI HAC,-HAC(P) ; Set to restore
BLT HAC,HAC ; Restore the AC's
ADJSP P,-<HAC+2> ; The the PDL pointer
POPJ P, ; then return
;Here to encrypt an IOWD on output
;Call with IOWD in T3
; PUSHJ P,CRPIOO
; <return here, does nothing if .OCODE EQL 0 or readonly mode>
CRPIOO: SKIPE OCODE ; Encrypting?
TRNE FL,READOF ; and not readonly?
POPJ P, ; No,
PUSHJ P,SAVCRX ; Yes, save AC's
MOVE 11,OCODE
MOVEI 10,OBUF+3
AOS 12,T3 ; Convert IOWD to AOBJN word and save it
PJRST CRPIOW
;Here to encrypt an IOWD on input
;Call with IOWD in T1 (not T3 as in CRPIOO)
; PUSHJ P,CRPIOI
; <return here, does nothing .ICODE EQL 0>
CRPIOI::SKIPN ICODE
POPJ P,
PUSHJ P,SAVCRX ;
MOVE 11,ICODE ; Input key
MOVEI 10,IBUF+3 ; Block counter
AOS 12,T1 ; Copy the IOWD
;
CRPIOW::MOVE 7,12 ; Get the IOWD
ADD 12,[BLKSIZ,,BLKSIZ] ; Anticipate next block
SKIPGE 12 ; If more than BLKSIZ words
HRLI 7,-BLKSIZ ; Use block size if too large
MOVE 5,11 ; Key
AOS 6,(10) ; Set up block number
PUSHJ P,CRYPT.## ; Encrypt this block
JUMPL 12,CRPIOW ; If more to do
POPJ P, ; Else return
;Here to encrypt a buffer on input
CRPBFI::SKIPN ICODE ; Needed?
POPJ P, ; No,
PUSHJ P,SAVCRX ; Save AC's
MOVEI 7,@CHNTAB+IN ; Locate the buffer header
HRRZ 7,.OPBUF(7) ; To the buffer header
HRRZ 7,.BFADR(7) ; Get the buffer address
ADD 7,[-BLKSIZ,,2] ; Make AOBJN word
AOS 6,IBUF+3
MOVE 5,ICODE ; The key itself
PJRST CRYPT.## ; Encrypt the block
CRPBFO::SKIPN OCODE ; See if needed
POPJ P, ; No,
PUSHJ P,SAVCRX ; Save AC's
MOVEI 7,@CHNTAB+OUT ; Locate the buffer header
HLRZ 7,.OPBUF(7) ; To the buffer header
HRRZ 7,.BFADR(7) ; Get the buffer address
ADD 7,[-BLKSIZ,,2] ; Make AOBJN word
AOS 6,OBUF+3 ; Block key (block count)
MOVE 5,OCODE ; The encryption key
PJRST CRYPT.## ; Call the encryptor
>; End of CRYPSW conditional
SUBTTL CKPNTR -- Debugging routine to watch pointers
IFN FTDEBUG,<
CKPNTR: TLNE PNTR,-1 ; Left half still zero?
PUSHJ P,CKPTR1 ; Nope
CAIG PNTR,(LPNTR) ; C(PNTR) GTR C(LPNTR)
PUSHJ P,CKPTR1 ; Nope, time to die
CAMG PNTR,BUFLIM ; High pointer points into space
CAMGE PNTR,BUFFIR ; Or fell in a hole?
PUSHJ P,CKPTR1 ; Time to die
POPJ P, ; All seems to be okay, return
; Here on a pointer failure. Better to go into DDT than just die
; if possible
CKPTR1: OUTSTR [ASCIZ/?Pointers check fails/]
SKIPN .JBDDT ; DDT in core?
JRST CKPTR2 ; No, just say what happened
OUTSTR [ASCIZ/--entering DDT
/]
PUSH P,.JBDDT## ; Put address of DDT on PDL
POPJ P, ; Enter DDT
HALT . ; He shouldn't have tried that
CKPTR2: OCRLF
EXIT 1,
>; End of FTDEBUG code for pointer checking
SUBTTL Disk space routines
; Subroutine to tell the user how much space is left on his disk area
;
TELSPC::PUSHJ P,FREDSK
TELSP0: MOVE T2,STRNAM+1 ; Get free space
JUMPL T2,TELOVR ; Overdrawn?
TELSP1: PUSHJ P,DPRNT## ; Print in decimal
OUTSTR [ASCIZ " blocks in your area on "]
PUSHJ P,GVDSTR## ; Give structure name
PUSHJ P,FOCRLF ; Terminate the line
MOVE T2,STRNAM+2 ; Get rest of world
PUSHJ P,DPRNT ; In decimal
OUTSTR [ASCIZ " blocks for all users"]
JRST FOCRLF## ; Force CRLF
TELOVR: OUTSTR [ASCIZ "Over quota by "]
MOVN T2,T2 ; Tell him bad news
JRST TELSP1
; Subroutine to obtain the amount of free disk space
FREDSK::TRNE FL,READOF ; Readonly?
JRST FREDS1 ; Yes, go get input structure
PUSHJ P,GOUSTR## ; Output structure name
JRST FREDS2 ; Join processing
FREDS1: PUSHJ P,GINSTR## ; Get input structure name
FREDS2: MOVEM T1,STRNAM
MOVE T1,[3,,STRNAM] ; DSKCHR UUO
DSKCHR T1,
SKIPA T1,[^D100] ; As good a number as any
SKIPG T1,STRNAM+1 ; Get amount in T1
ADDI T1,OVRDRW ; Adjust for overdraw
CAMLE T1,STRNAM+2 ; How about the rest of the world
MOVE T1,STRNAM+2 ; Disk is full - give lesser
POPJ P,
END