;<PUP>PUPXFR.MAC;6 2-SEP-79 16:01:37 EDIT BY TAFT ;<PUP>PUPXFR.MAC;5 1-SEP-77 16:14:07 EDIT BY TAFT ; Make a few things internal for the mail server ;<PUP>PUPXFR.MAC;4 18-MAR-77 19:44:47 EDIT BY TAFT ; Fix bugs in paged transfer code ;<PUP>PUPXFR.MAC;3 18-MAR-77 17:07:39 EDIT BY TAFT ; Remove ## from property list offset symbols ;<PUP>PUPXFR.MAC;2 15-MAR-77 18:56:49 EDIT BY TAFT ; Implement Tenex-paged transfer type ;<PUP>PUPXFR.MAC;1 10-MAR-77 14:05:22 EDIT BY TAFT ; Split out from PUPFTP.MAC ; Copyright 1979 by Xerox Corporation TITLE PUPXFR -- DATA TRANSFER CODE FOR FTP USER AND SERVER SUBTTL E. A. Taft / March 1977 SEARCH PUPDEF,STENEX USEVAR FTPVAR,FTPPVR ; Receive data from net to local file ; Assumes SRCJFN, DSTJFN setup and local file open ; A/ Property list pointer ; Returns +1: Data error during Retrieve ; +2: Retrieve completed successfully ; No messages generated ; JFN still open ; Clobbers A-D RECDAT::PUSHJ P,SAVE1## MOVE P1,A ; Preserve property list pointer MOVEI A,NETBUF## ; Source is net MOVEM A,SRCIBP MOVEI A,8 ; Bytesize always 8 PUSHJ P,SETINP ; Setup for input MOVEI A,RECNDE ; Set dispatch for net data error MOVEM A,SRCDSP## MOVEI A,FILBUF## ; Destination is local file system MOVEM A,DSTIBP HRRZ A,P.BYTE(P1) ; Bytesize as specified PUSHJ P,SETOUT ; Setup for output MOVEI A,RECFDE ; Set dispatch for file data error MOVEM A,DSTDSP## MOVEM P,ERRPDP## ; Save stack level for error HRRZ A,P.BYTE(P1) ; Get type and bytesize again HRRZ B,P.TYPE(P1) MOVEI D,PAGREC ; Assume paged CAIN B,3 ; Check type JRST RECDA5 ; Paged CAIN B,2 JRST RECDA3 ; Binary MOVEI D,TXTREC ; Text, assume have to convert HRRZ C,P.EOLC(P1) ; Get end-of-line convention JUMPE C,RECDA5 ; Jump if must convert CR to CRLF RECDA3: MOVEI D,BINBYT ; Assume can do byte-at-a-time CAIN A,8 ; Local byte size 8? MOVEI D,BINPAG ; Yes, can do page-at-a-time CAILE A,8 ; Local byte size more than 8? MOVEI D,MULREC ; Yes, must do multi-byte shuffle RECDA5: PUSHJ P,0(D) ; Do the transfer PUSHJ P,PUTLPG ; Force out last buffer if required PUSHJ P,UNMAP ; Unmap window page if any DTYPE <%/> ; Eol after "!"s if debugging JRST SKPRET## ; Give +2 return ; Here if I/O data error occurs while writing into local file RECFDE: PUSHJ P,UNMAP ; Unmap window page if any HRRZ A,DSTJFN ; Get destination JFN SETZ B, ; Clear error flag if on STSTS CAI POPJ P, ; Give +1 return ; Here if I/O data error occurs while reading from net RECNDE: PUSHJ P,UNMAP ; Unmap window page if any PUSHJ P,KILFIL## ; Try to eliminate the file MOVEI A,400000 ; Force unenabled data error PSI MOVSI B,(1B11) IIC PUSHJ P,SCREWUP## ; Send data from local file to net ; Assumes SRCJFN, DSTJFN setup and local file open ; A/ Property list pointer ; Returns +1: Data error ; +2: Transfer completed successfully ; No reply messages generated ; JFN still open ; Clobbers A-D SNDDAT::PUSHJ P,SAVE1## MOVE P1,A ; Preserve property list pointer MOVEI A,FILBUF## ; Source is local file system MOVEM A,SRCIBP HRRZ A,P.BYTE(P1) ; Bytesize as specified PUSHJ P,SETINP ; Setup for input MOVEI A,SNDDTE ; Set input error dispatch MOVEM A,SRCDSP## MOVEM P,ERRPDP## ; Save stack level MOVEI A,NETBUF## ; Destination is net MOVEM A,DSTIBP MOVEI A,8 ; Bytesize always 8 PUSHJ P,SETOUT ; Setup for output SETZM DSTDSP## ; Do not enable for output errors HRRZ A,P.BYTE(P1) ; Get type and bytesize again HRRZ B,P.TYPE(P1) MOVEI D,PAGSND ; Assume paged CAIN B,3 ; Check type JRST SNDDA5 ; Paged CAIN B,2 JRST SNDDA3 ; Binary MOVEI D,TXTSND ; Text, assume have to convert HRRZ C,P.EOLC(P1) ; Get end-of-line convention JUMPE C,SNDDA5 ; Jump if must convert CRLF to CR SNDDA3: MOVEI D,BINBYT ; Assume can do byte-at-a-time CAIN A,8 ; Local byte size 8? MOVEI D,BINPAG ; Yes, can do page-at-a-time CAILE A,8 ; Local byte size more than 8? MOVEI D,MULSND ; Yes, must do multi-byte shuffle SNDDA5: PUSHJ P,0(D) ; Do the transfer PUSHJ P,PUTLPG ; Force out last buffer if required PUSHJ P,UNMAP ; Unmap window page if any DTYPE <%/> ; Eol after "!"s if debugging JRST SKPRET## ; Return +2 ; Here if I/O data error occurs while reading from local file SNDDTE: PUSHJ P,UNMAP ; Unmap window page if any HRRZ A,SRCJFN ; Get source JFN SETZ B, ; Clear error flag if on STSTS CAI POPJ P, ; Return +1 ; Routines implementing the various types of data transfer. ; Assume that source and destination parameters are set up ; and that P1 points to the file property list. ; Return +1 always ; Clobber A-D ; Transfer binary data page at a time (no conversion) BINPAG: HRRZ A,SRCIBP ; Use same buffer for destination HRRM A,DSTIBP ; as for source HRRM A,DSTBYT BINPA1: PUSHJ P,GETPAG ; Get a page of data POPJ P, ; No more PUSHJ P,PUTPAG ; Output the data we got JRST BINPA1 ; Repeat ; Transfer binary data byte at a time (no conversion) BINBYT: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store in output buffer JRST BINBYT ; Repeat ; Receive text from net (CR to CRLF conversion) TXTREC: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte TXTRE1: SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store in output buffer CAIE A,15 ; Carriage return? JRST TXTREC ; No, not special MOVEI A,12 ; Yes, append line feed PUSHJ P,PUTBYT PUSHJ P,GETBYT ; Get next byte POPJ P, ; No more CAIN A,12 ; A line feed? JRST TXTREC ; Yes, don't double it JRST TXTRE1 ; No, handle normally ; Send text to net (CRLF to CR conversion) TXTSND: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store in output buffer CAIE A,15 ; Carriage return? JRST TXTSND ; No, not special PUSHJ P,GETBYT ; Yes, get next byte POPJ P, ; No more CAIE A,12 ; Line feed? PUSHJ P,PUTBYT ; No, store it JRST TXTSND ; Repeat ; Receive data from net with multiple-byte shuffle ; Each local byte received right-justified in an integral number ; of network bytes MULREC: PUSHJ P,SAVE1## ; Get another ac HRRZ A,P.BYTE(P1) ; Get local byte size ADDI A,7 ; Compute net bytes per local byte IDIVI A,8 HRLM A,0(P) ; Save it ; Loop here for each local byte MULRE1: HLRZ B,0(P) ; Get net bytes/local byte JRST @MULRTB-2(B) ; Dispatch to code to receive bytes MULRTB: MULRE2 ; 2 net bytes/local byte MULRE3 ; 3 MULRE4 ; 4 MULRE5 ; 5 MULRE5: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte LSH P1,8 ; Make room in local byte IORI P1,(A) ; Append new byte MULRE4: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte LSH P1,8 ; Make room in local byte IORI P1,(A) ; Append new byte MULRE3: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte LSH P1,8 ; Make room in local byte IORI P1,(A) ; Append new byte MULRE2: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte LSH P1,8 ; Make room in local byte IORI P1,(A) ; Append new byte SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte LSH P1,8 ; Make room in local byte IOR A,P1 ; Append new byte SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer JRST MULRE1 ; Repeat for next local byte ; Send data to net with multiple-byte shuffle ; Each local byte sent right-justified in an integral number ; of network bytes MULSND: HRRZ A,P.BYTE(P1) ; Get local byte size ADDI A,7 ; Compute net bytes per local byte IDIVI A,8 HRLM A,0(P) ; Save it ; Loop here for each local byte MULSN1: SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte HLRZ B,0(P) ; Recover net bytes/local byte XCT ROTTBL(B) ; Right-justify first net byte JRST @MULSTB-2(B) ; Branch to code to send bytes ROTTBL: PUSHJ P,SCREWUP ; 0 net bytes/local byte PUSHJ P,SCREWUP ; 1 ROT A,-8 ; 2 ROT A,-↑D16 ; 3 ROT A,-↑D24 ; 4 ROT A,-↑D32 ; 5 MULSTB: MULSN2 ; 2 net bytes/local byte MULSN3 ; 3 MULSN4 ; 4 MULSN5 ; 5 MULSN5: SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer ROT A,8 ; Prepare next byte MULSN4: SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer ROT A,8 ; Prepare next byte MULSN3: SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer ROT A,8 ; Prepare next byte MULSN2: SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer ROT A,8 ; Prepare next byte SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, dump buffer (return .-1) IDPB A,DSTBYT ; Store byte in output buffer JRST MULSN1 ; Loop for next local byte ; Receive Tenex-paged file PAGREC: PUSHJ P,SAVE3## ; Save ac's used by coroutine MOVEI P1,RECCOR ; Initialize coroutine MOVE P3,[-25,,FILBUF##] ; Receive FDB PUSHJ P,GETBLK JRST ILLREC ; Eof at bad time ; Copy selected fields from the received FDB MOVEI D,NORTBL ; Pointer to field descriptor table MOVE P3,0(D) ; Get byte pointer PAGRE1: ILDB A,P3 ; Get FDB offset JUMPE A,PAGRE2 ; Jump if done MOVE B,1(D) ; Get word mask MOVE C,FILBUF##(A) ; Get data from received FDB HRLZ A,A ; Put offset in lh HRR A,DSTJFN ; File JFN CHFDB ; Set the FDB word AOJA D,PAGRE1 ; Repeat ; Receive next page of file PAGRE2: JSP P1,0(P1) ; Get protection,,page # POPJ P, ; No more PUSH P,A ; Save them MOVE P3,[-1000,,FILBUF##] ; Get page of data PUSHJ P,GETBLK JRST ILLREC ; Eof at bad time MOVEI A,FILBUF## ; Get window address in this fork LSH A,-9 ; Convert to page number HRLI A,400000 ; This fork HRRZ B,0(P) ; File page number HRL B,DSTJFN ; File JFN MOVSI C,(1B3) ; Give us write access PMAP ; Put fork page in file MOVE A,B ; File JFN,,page POP P,B ; Recover protection SPACS ; Set page protection PUSHJ P,UNMAP ; Remove page from fork's map JRST PAGRE2 ; Repeat for next page ; Here if EOF occurs at a bad time. Force data error PSI ILLREC: MOVEI A,400000 ; This fork MOVSI B,(1B11) IIC ; Initiate PSI PUSHJ P,SCREWUP## ; Should never return ; Table describing fields to copy from received FDB ; Normal attribute retention: NORTBL: POINT 5,[BYTE(5) 11,12,0] 7700,,0 ; FDBBYV: byte size -1 ; FDBSIZ ; May add other tables later for additional attribute retention ; Receive block of 36-bit data from net ; P3/ -length,,address of block ; P1, P2/ set up for coroutine ; Returns +1: EOF before block exhausted ; +2: Normal ; Clobbers A-D, P3 GETBLK: JSP P1,0(P1) ; Coreturn for next word POPJ P, ; End of file MOVEM A,0(P3) ; Store in block AOBJN P3,GETBLK ; Loop thru block JRST SKPRET## ; Return +2 ; Receive coroutine ; Uses P1 for coroutine linkage, P2 for -word count,,pointer. ; JSP P1,0(P1) ; Returns +1: end of file ; +2: normal, A/ data word ; Clobbers A-D; updates P1, P2 ; Coroutine initially starts here RECCOR: MOVE B,SRCBPP ; Get nominal bytes per page IDIVI B,↑D20 ; Length of cycle is 20 bytes IMULI B,↑D20 MOVEM B,SRCBPP ; Store actual bytes per buffer JRST RECNPG ; Go read first buffer ; Top of loop RECCO0: AOBJP P2,RECNPG ; Jump if buffer empty MOVE A,-1(P2) ; Get first 28 bits MOVE B,0(P2) ; Get last 8 bits LSH A,-4 ; Concatenate LSHC A,8 JSP P1,1(P1) ; Coreturn +2 AOBJP P2,RECNPG ; Jump if buffer empty MOVE A,-1(P2) ; Get first 20 bits MOVE B,0(P2) ; Get last 16 bits LSH A,-4 ; Concatenate LSHC A,↑D16 JSP P1,1(P1) ; Coreturn +2 AOBJP P2,RECNPG ; Jump if buffer empty MOVE A,-1(P2) ; Get first 12 bits MOVE B,0(P2) ; Get last 24 bits LSH A,-4 ; Concatenate LSHC A,↑D24 JSP P1,1(P1) ; Coreturn +2 AOBJP P2,RECNPG ; Jump if buffer empty MOVE A,-1(P2) ; Get first 4 bits MOVE B,0(P2) ; Get last 32 bits LSH A,-4 ; Concatenate LSHC A,↑D32 JSP P1,1(P1) ; Coreturn +2 AOJA P2,RECCO0 ; Back to top of loop ; Here when buffer empty, get another RECNPG: PUSHJ P,GETPAG ; Get another buffer from net JRST 0(P1) ; No more, return +1 IDIVI C,5 ; Number of words in network format HRLOI P2,0(C) ; Make -(word count +1),,pointer EQVI P2,0(B) JRST RECCO0 ; Back to top of coroutine ; Send Tenex-paged file PAGSND: PUSHJ P,SAVE3## ; Save ac's used by coroutine MOVEI P1,SNDCOR ; Initialize coroutine HRRZ A,SRCJFN ; Get FDB for local file MOVSI B,25 ; The whole thing MOVEI C,FILBUF## ; Put it here GTFDB MOVE P3,[-25,,FILBUF##] ; Send FDB to net PUSHJ P,PUTBLK HRROS 0(P) ; Init previous page # to -1 ; Send next page of file PAGSN2: PUSHJ P,SETWDT## ; Reset watchdog timer DTYPE <!> ; Signal progress if debugging HLRZ A,0(P) ; Get previous page number ADDI A,1 ; Increment HRL A,SRCJFN FFUFP ; Find next used page JRST FIXCNT ; No more, fix count for flush HRLM A,0(P) ; Save page number MOVEI B,FILBUF## ; Get window address in this fork LSH B,-9 ; Convert to page number HRLI B,400000 ; This fork MOVSI C,(1B2) ; Give us read access PMAP ; Map fork page into file RPACS ; Get page protection HLL A,B ; Make protection,,page # JSP P1,0(P1) ; Send it MOVE P3,[-1000,,FILBUF##] ; Send page of data PUSHJ P,PUTBLK JRST PAGSN2 ; Repeat for next page ; Send block of 36-bit data to net ; P3/ -length,,address of block ; P1, P2/ set up for coroutine ; Returns +1 always ; Clobbers A-D, P3 PUTBLK: MOVE A,0(P3) ; Get word from block JSP P1,0(P1) ; Coreturn to send next word AOBJN P3,PUTBLK ; Loop thru block POPJ P, ; Unmap window page when done with file (so we can close it) ; Returns +1 ; Clobbers A, B UNMAP:: SETO A, MOVEI B,FILBUF## ; File buffer address LSH B,-9 ; Convert to page number HRLI B,400000 ; This fork PMAP ; Unmap page POPJ P, ; Send coroutine ; Uses P1 for coroutine linkage, P2 for -word count,,pointer. ; A/ data word ; JSP P1,0(P1) ; Returns +1 always ; Clobbers A-D; updates P1, P2 ; Coroutine initially starts here SNDCOR: PUSH P,A ; Save data MOVE B,DSTBPP ; Get nominal bytes per page IDIVI B,↑D20 ; Length of cycle is 20 bytes IMULI B,↑D20 MOVEM B,DSTBPP ; Store actual bytes per buffer MOVEM B,DSTCNT ; No bytes actually stored yet JRST SNDNP0 ; Go setup first buffer ; Top of loop SNDCO0: AOBJP P2,SNDNPG ; Jump if buffer full SETZ B, LSHC A,-4 MOVEM A,-1(P2) ; Store first 28 bits LSHC A,-4 MOVEM B,0(P2) ; Store last 8 bits JSP P1,0(P1) ; Coreturn AOBJP P2,SNDNPG ; Jump if buffer full SETZ B, LSHC A,-↑D12 IORM A,-1(P2) ; Store first 20 bits LSHC A,-4 MOVEM B,0(P2) ; Store last 16 bits JSP P1,0(P1) ; Coreturn AOBJP P2,SNDNPG ; Jump if buffer full SETZ B, LSHC A,-↑D20 IORM A,-1(P2) ; Store first 12 bits LSHC A,-4 MOVEM B,0(P2) ; Store last 24 bits JSP P1,0(P1) ; Coreturn AOBJP P2,SNDNPG ; Jump if buffer full LSHC A,-↑D28 IORM A,-1(P2) ; Store first 4 bits LSHC A,-4 MOVEM B,0(P2) ; Store last 32 bits JSP P1,0(P1) ; Coreturn AOJA P2,SNDCO0 ; Back to top of loop ; Here when buffer full, send it and start another. SNDNPG: PUSH P,A ; Save data SUB P2,[1,,1] ; Correct word count PUSHJ P,FIXCNT ; Recompute byte count SNDNP0: PUSHJ P,PUTLPG ; Send another buffer to net IDIVI C,5 ; Number of words in network format HRLOI P2,0(C) ; Make -(word count +1),,pointer EQVI P2,0(B) POP P,A ; Recover data JRST SNDCO0 ; Back to top of coroutine ; Fix up output byte count (DSTCNT) to be consistent with count ; of words actually stored ; P2/ -(word count +1),,pointer ; Returns +1 ; Clobbers A FIXCNT: HLRE A,P2 ; Get -(word count+1) MOVNI A,1(A) ; Compute word count in RH IMULI A,5 ; Byte count HRRZM A,DSTCNT ; Store in standard place POPJ P, ; Setup to do input ; A/ Byte size ; Assumes SRCJFN setup ; Returns +1 ; Clobbers B, C SETINP::MOVEI B,4400(A) ; Setup initial byte pointer LSH B,6 HRLM B,SRCIBP MOVEI B,↑D36 ; Compute bytes per word IDIVI B,(A) LSH B,9 ; Compute bytes per page MOVEM B,SRCBPP ; Store bytes per page SETZM SRCCNT ; No bytes yet in buffer POPJ P, ; Get next byte of data from input file ; Returns +1: End of file ; +2: A/ the byte ; Clobbers A-D GETBYT::SOSGE SRCCNT ; Count down source bytes PUSHJ P,GETNPG ; None, get new page (return .-1) ILDB A,SRCBYT ; Get the byte JRST SKPRET## ; Return +2 ; Get next page of data from input file (special call) ; Returns to caller-1 on success ; Returns to caller of caller +1 on end of file ; Clobbers A-D GETNPG::PUSHJ P,GETPAG ; Get page from input file JRST [ SUB P,[1,,1] ; No more, flush caller POPJ P,] ; Return to caller of caller POP P,D ; Got some, pop off return JRST -2(D) ; Return to caller-1 ; Get a page of data from input file ; Returns +1: End of file ; +2: B/ Byte pointer, C/ Byte count ; Also setup in SRCBYT and SRCCNT respectively ; Clobbers A-D GETPAG::PUSHJ P,SETWDT## ; Reset watchdog timer DTYPE <!> ; Signal progress if debugging HRRZ A,SRCJFN ; Get source JFN MOVE B,SRCIBP ; Get source buffer byte ptr MOVN C,SRCBPP ; Get negative of bytes/page HRROS SRCDSP ; Enable data error dispatch SIN ; Read bytes from source HRRZS SRCDSP ; Disable data error dispatch ADD C,SRCBPP ; Compute # bytes read JUMPLE C,CPOPJ## ; Return +1 if none (eof) MOVEM C,SRCCNT MOVE B,SRCIBP ; Setup source buffer byte ptr MOVEM B,SRCBYT JRST SKPRET## ; Return +2 ; Setup to do output ; A/ Byte size ; Assumes DSTJFN setup ; Returns +1 ; Clobbers B, C SETOUT::MOVEI B,4400(A) ; Setup initial byte pointer LSH B,6 HRLM B,DSTIBP MOVE B,DSTIBP ; Copy to current byte pointer MOVEM B,DSTBYT SETZM 0(B) ; Clear the buffer so that leftover MOVSI C,0(B) ; bits in byte-by-byte transfers HRRI C,1(B) ; stay zero BLT C,777(B) MOVEI B,↑D36 ; Compute bytes per word IDIVI B,(A) LSH B,9 ; Compute bytes per page MOVEM B,DSTBPP ; Store bytes per page MOVEM B,DSTCNT ; Set initial count POPJ P, ; Put next byte of data into output file ; A/ the byte ; Returns +1 always ; Clobbers A-D PUTBYT::SOSGE DSTCNT ; Count down destination bytes PUSHJ P,PUTNPG ; No more, write page (return .-1) IDPB A,DSTBYT ; Ok, store the byte POPJ P, ; Return ; Put next page of data on output file (special call) ; Assumes one excess "SOSGE DSTCNT" has been done ; Returns to caller-1 always ; Clobbers B-D; preserves A PUTNPG::PUSH P,A ; Save possible data being stored MOVE C,DSTBPP ; Get bytes/page SUB C,DSTCNT ; Compute actual # bytes stored SUBI C,1 PUSHJ P,PUTPAG ; Put page on output file POP P,A ; Restore data POP P,D ; Pop off return JRST -2(D) ; Return to caller-1 ; Output last (partial) page if necessary ; Assumes DSTCNT is correct ; Returns +1 ; Clobbers A-D PUTLPG::MOVE C,DSTBPP ; Get bytes/page SUB C,DSTCNT ; Compute actual # bytes in buffer ; Fall into PUTPAG ; Put a page of data on output file ; C/ # of data bytes in page ; Returns +1 always ; B/ Byte ptr, C/ Byte count for buffer ; Also setup in DSTBYT and DSTCNT respectively ; Clobbers A-D PUTPAG::JUMPLE C,PUTPA1 ; Do nothing if none HRRZ A,DSTJFN ; Get destination JFN MOVE B,DSTIBP ; Get destination buffer byte ptr MOVN C,C ; Make negative byte count HRROS DSTDSP ; Enable data error dispatch SOUT ; Write bytes on destination HRRZS DSTDSP ; Disable data error dispatch PUTPA1: MOVE C,DSTBPP ; Setup destination bytes/page MOVEM C,DSTCNT MOVE B,DSTIBP ; Setup destination buffer byte ptr MOVEM B,DSTBYT POPJ P, ; Return ; Storage LS SRCJFN ; Source JFN LS SRCIBP ; Source buffer byte pointer LS SRCBPP ; Source bytes/page LS SRCBYT ; Current byte ptr LS SRCCNT ; Remaining bytes in buffer LS DSTJFN ; Destination JFN LS DSTIBP ; Destination buffer byte pointer LS DSTBPP ; Destination bytes/page LS DSTBYT ; Current byte ptr LS DSTCNT ; Remaining bytes in buffer END