Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_3_19910112
-
stanford/ftp/tcpxfr.mac
There are no other files named tcpxfr.mac in the archive.
;<FTP>TCPXFR.MAC.143, 5-Mar-85 19:18:30, Edit by LOUGHEED
; Zero last page on a paged receive so that the EXEC's checksumming
; routine works on files received in such a fashion.
;<FTP>TCPXFR.MAC.142, 29-Oct-84 16:23:20, Edit by LOUGHEED
; From Rutgers: send file attributes according to KEEP parameters
;<FTP>TCPXFR.MAC.141, 21-Aug-84 18:00:49, Edit by KRONJ
; Try slight rearrangement in T36GET so that P1 doesn't get messed
; up at end of buffer refill by the ADDI P1,9 at T36TOP
;<FTP>TCPXFR.MAC.140, 27-Apr-84 14:54:04, Edit by KRONJ
; Clean up paged transfer code a little - use symbolic names for page
; types and for offsets in page block header. I know, if it ain't broke...
;<FTP>TCPXFR.MAC.139, 26-Apr-84 21:59:38, Edit by KRONJ
; Not quite counting bits correctly in T36GET
; (was screwing paged receives of more than 770 words of data)
SEARCH FTPDEF
TTITLE(TCPXFR, -- Low-level data transfer for user/server TCP FTP)
SUBTTL David Eppstein / Stanford University / 2-Feb-1984
;; Copyright (C) 1984 Board of Trustees, Stanford University
;; The information in this software is subject to change without
;; notice and should not be construed as a commitment by Stanford
;; University. Stanford assumes no responsibility for the use or
;; reliability of this software.
EXTERN SHXINI,SHXUMP,SHXEND,UNMAP,TCXCLS,TCXABT,FILJFN
EXTERN FILPRP,NETBUF,NETJFN,BCOUNT,FILBSZ,NBYTES,FILPAG,FILBYT
EXTERN SFBSZ,PUTPAG,FILPTR,NBFPTR,NETPAG,NXTPAG,TEMP,NETBYT,NETROT
EXTERN SETFDB,SETPAG,PADCHR,NETPSV,NETHDR,INPPAG,SETBSZ,GETPAG
LS TCXJFN ; JFN for data connection
LS MODCOR ; Which coroutine to use for net I/O
; Definitions for paged transfer header
; these are DEFINE rather than real constants because FILPAG is EXTERN
DEFINE PAGHDR <FILPAG-5> ; Header starts five words before file data
DEFINE HDRLEN <FILPAG-5> ; First word is number of header words
DEFINE PAGNO <FILPAG-4> ; Second word is number of page in file
DEFINE DATLEN <FILPAG-3> ; Third word is number of words of data
DEFINE PAGTYP <FILPAG-2> ; Fourth word is type of page
PGT$LP==:0 ; Last page
PGT$SP==:1 ; Simple data page
PGT$DP==:2 ; Descriptor page
PGT$AP==:3 ; Access controlled page
DEFINE PAGACS <FILPAG-1> ; Fifth word is page access (known by SETPAG)
; Table of coroutines, indexed by mode number
MODCTB::TCSBLK,,TCRBLK ; MD.ABK auto-block uses block unless can't
TCSSTR,,TCRSTR ; MD.STR stream mode
TCSBLK,,TCRBLK ; MD.BLK block mode
TCSCMP,,TCRCMP ; MD.CMP compressed mode
SUBTTL Reception of data from net
; TCXREC - Receive file from net
; takes opened JFN in FILJFN, file properties in FILPRP
; TCPSET and TCXOPN must already have been done
; TCXCLS will be done if necessary
; returns +1/failure, +2/success
TCXREC::SAVEAC <P1> ; Save register to use for coroutine
HRRZ P1,MODCOR ; Get output coroutine to use
CALL SHXINI ; Start typing excls
SETZM NBYTES ; No bytes put yet
SETOM NXTPAG ; No pages either
MOVE A,FILPRP+P.TYPE
JRST @[ TCRBIN ; TT.UNS unspecified - image
TCRTXT ; TT.TXT text
TCRBIN ; TT.BIN binary
TCRPAG ; TT.PAG paged
TCRDIR ; TT.DIR directory
TCRPAG ; TT.MEI other style paged
TCREBC ; TT.EBC EBCDIC
TCRBIN ](A) ; TT.IMG image (treated as binary)
TCRDIR: TYPE <%% Directory transfers not possible%/>
RET
; Receive EBCDIC transfer
TCREBC: SAVEAC <P2> ; Use this register
MOVEI P2,TREBCC ; Get coroutine
MVI. 5000,BCOUNT ; One page of 5 bytes per word
MVI. 7,FILBSZ ; 7 bits per byte
MVI. <-1,,FILPAG>,FILPTR ; Into where PUTPAG expects it
DO.
SETZM FILBYT ; Don't count bytes yet
MOVE B,[POINT 7,FILPAG] ; Point to start of page
JSP P2,(P2) ; Get data
JRST SFBSZ ; No more
JSP P2,(P2) ; Get other half of data
NOP ; Got some, make sure page goes out
CALL PUTPAG ; Send them off
LOOP. ; Back for next page
ENDDO.
TREBCC: PUSH P,B ; Save pointer
MOVE B,[POINT 8,NETPAG] ; Point to another buffer
MOVNI C,2400 ; Half a pageful of ASCII
JSP P1,(P1) ; Get some
JRST TREBCR ; Done
ADDM C,FILBYT ; Count some bytes
ADDI C,2400 ; Get number of bytes
POP P,B ; Get pointer back
MOVE D,[POINT 8,NETPAG] ; and point to start of net page
TREBCL: ILDB A,D ; Get EBCDIC byte
HRRZ A,EBCTAB(A) ; Convert to ASCII
IDPB A,B ; Deposit in receive buffer
SOJG C,TREBCL ; Count off and go back for more
JSP P2,1(P2) ; Return success
JRST TREBCC ; Back for another load
TREBCR: POP P,B ; Get pointer back
TREBCZ: JSP P2,(P2) ; Return +1 for out of data
JRST TREBCZ ; Allow caller to try again but don't give any
; Receive paged transfer
TCRPAG: SAVEAC <P2> ; Save register for coroutine
XMOVEI P2,T36COR ; Get 36-bit coroutine
MVI. 1000,BCOUNT ; Set count of bytes per page
MVI. ^D36,FILBSZ ; And size of each byte
; Here to read the next paged block of data from the connection
TCRPGL: MOVE D,[-4,,PAGHDR] ; Set up pointer for start of header
JSP P2,(P2) ; Read that much in
JRST SHXEND ; No more data, done
;; Set up access control word from header or make it up if not given
MOVE D,HDRLEN ; Get header count
CAIE D,5 ; If we have an access word
IFSKP.
HRROI D,PAGACS ; Point to it
JSP P2,(P2) ; Read that much in
JRST TCRPGE ; Unexpected end of data
ELSE.
SETOM PAGACS ; Not access controlled, use all ones
CAIE D,4 ; Header length not 5, must be 4
JRST TCRPGH ; Not 4 or 5, complain about strange length
ENDIF.
;; Now read in page data
SKIPN D,DATLEN ; Get number of words in this page
JRST TCRPG0 ; No data, so go figure out what to do
CAIN D,1000 ; Not a full page of data?
IFSKP.
SETZM FILPAG ; Yes, clear first word of page
MOVE A,[XWD FILPAG, FILPAG+1] ; Set up BLT pointer
BLT A,FILPAG+777 ; Ensure zeros on unused part of page
ENDIF.
MOVNS D ; Negate the word count
HRLZS D ; Into left half
HRRI D,FILPAG ; Into file page
JSP P2,(P2) ; Read page contents
JRST TCRPGE ; Unexpected end of data
;; Page all read, figure out what to do with it
TCRPG0: SKIPN D,PAGTYP ; Last page?
JRST TCRPGZ ; Yes
CAIE D,PGT$SP ; Simple page
CAIN D,PGT$AP ; or access controlled page?
JRST TCRPGP ; Yes
CAIN D,PGT$DP ; FDB?
JRST TCRPGD ; Yes
TYPE <%_%% Unknown page type %4D in paged transfer, ignored%/>
JRST TCRPGL ; Don't do anything with strange type
; Here with an actual page of data
TCRPGP: MOVE D,PAGNO ; Get page number
HRRM D,PAGACS ; Add to access bits where SETPAG expects it
CALL SETPAG ; Put the page into the file
JRST TCRPGL ; Back for another page
; Here when the header length was other than 4 or 5
TCRPGH: ETYPE <Unexpected length %4D for paged header%/>
JRST TCXABT ; Lose lose
; Here when we didn't get as much data as we expected there to be
TCRPGE: ETYPE <Unexpected end of data in paged connection%/>
JRST TCXABT ; Flush data conn
; Here for descriptor page PGT$DP, i.e. FDB
TCRPGD: CALL SETFDB ; Go set FDB up
JRST TCRPGL ; Back for another page
; Here when page type is PGT$LP, last page
TCRPGZ: SKIPE D,DATLEN ; Yes, any data?
TYPE <%_%% %4D words of data on terminal page%/>
JRST SHXEND ; Done with reading pages
; Receive binary or image transfer
; we tell the server different things for binary and image,
; but treat them the same ourself - packed bits to be
; broken into bytes.
TCRBIN: SKIPN B,FILPRP+P.BYTE ; Get byte size
MOVEI B,^D8 ; Eight if not specified
MOVEM B,FILBSZ ; Save
MOVE C,B ; Copy
LSH C,^D24 ; Shift into place
IOR C,[440000,,FILPAG] ; To make byte pointer
MOVEM C,FILPTR ; Save
MOVEI C,^D36 ; Get number of bits in a word
IDIVI C,(B) ; Divide by number of bits in a byte
IMULI C,1000 ; And multiply by number of words in a page
MOVEM C,BCOUNT ; To get number of bytes in a page
JRST @[ REPEAT ^D4,<TCRB36> ; 1 through 4 bits fit into 36
TCRBSH ; 5 bits - shuffle
TCRB36 ; 6 bits fit into 36
TCRBSH ; 7 bits - shuffle
TCRB8 ; 8 bits don't need any shuffling
TCRB36 ; 9 bits fit into 36
TCRBSH ; 10 bits - shuffle
TCRBSH ; 11 bits - shuffle
TCRB36 ; 12 bits fit into 36
REPEAT ^D3,<TCRBSH> ; 13 through 15 bits - shuffle
TCRB8 ; 16 bits don't need any shuffling
TCRBSH ; 17 bits - shuffle
TCRB36 ; 18 bits fit into 36
REPEAT ^D13,<TCRBSH> ; 19 through 31 bits - shuffle
TCRB8 ; 32 bits don't need any shuffling
REPEAT ^D3,<TCRBSH> ; 33 through 35 bits - shuffle
TCRB36 ]-1(B) ; 36 bits fit into 36
; Here for 8, 16, 32-bit logical bytes - read in 8-bit, send without shuffle
TCRB8: SAVEAC <P2> ; Use this register
JFFO B,.+2 ; Find number of bit
FATAL <No bits at TCRB8>
HRREI P2,-^D32(C) ; Get shift factor
DO.
MOVE B,[POINT 8,FILPAG] ; With appropriate pointer
MOVNI C,4000 ; And appropriate number of bytes
JSP P1,(P1) ; Get some data
JRST SFBSZ ; Done, finish up
LSH C,(P2) ; Shift count appropriately
MOVEM C,FILBYT ; Save number of bytes for PUTPAG
CALL PUTPAG ; Send them off
LOOP. ; Back for next page
ENDDO.
; Binary transfer for random byte sizes
; NETBYT contains any bits left over from previous words, right-aligned
; NBFPTR contains a byte pointer to the next place to put a word
; NETROT contains the number of data bits in NETBYT
; This code is intentionally not very efficient.
; I don't expect it to be run very often - in fact perhaps
; not for years. It is much more important for it to run bug-free
; than for it to run quickly, as by the time any bug is noticed
; there may be no one around to fix it.
; Main entry point
TCRBSH: SAVEAC <P2> ; Use another register
SETZM NETBYT ; No bits left over
SETZM NETROT ; Zero of them
JRST TCRBS0 ; Start new page
; Here from TCRBS3 when we didn't have enough data for a full word.
; Read in another set of bytes from the data connection.
; First we have to store away the rest of the old data for later.
TCRBS4: MOVE B,A ; Copy count of total bits
SUB B,NETROT ; Don't include the old ones
LSHC C,(B) ; Shift them all into one word
MOVEM A,NETROT ; Save as new count of bits
MOVEM C,NETBYT ; and extra bits
;; Old data set, now get some new stuff
MOVE B,[POINT 8,P2] ; Drop bits into this register
MOVNI C,4 ; Four whole bytes
JSP P1,(P1) ; Get net data
JRST TCRBS2 ; No more, done
MOVEI A,4(C) ; Make number of bytes received
LSH A,3 ; As number of bits
ADD A,NETROT ; As total bits of this word and prev
MOVE C,NETBYT ; Get previous bits word
MOVE D,P2 ; and current bits
; JRST TCRBS3 ; Go store word if we now have enough
; Here to put data
; A/total bits left, NETROT/bits in first word, C+D/the bits
; send them off to NBFPTR until we run out of bits or fill a page
TCRBS3: CAIGE A,FILBSZ ; Do we have enough for one byte?
JRST TCRBS4 ; No, go store data out
;; Have enough for a whole word, make one and store it
MOVE B,FILBSZ ; Get count
SUB B,NETROT ; Not including what we have now
SUB A,FILBSZ ; Count off from total bits left
LSHC C,(B) ; Shift word in
IDPB C,NBFPTR ; Deposit byte
SETZB C,NETROT ; Clear bits for next byte
AOSGE FILBYT ; Count off a byte sent
JRST TCRBS3 ; Page not full yet, get another byte
;; Have filled a page, store values back for later and send off
;; all the bits are currently in D
LSHC C,(A) ; Shift rest of bits in
MOVEM C,NETBYT ; Save as new leftover bits
MOVEM A,NETROT ; and save how many of them there are
CALL PUTPAG ; Send page off to file
; JRST TCRBS0 ; Back to start of a new page
; Here to set up vars to fill start of a new input page.
TCRBS0: MV. FILPTR,NBFPTR ; Point to start of buffer
MOVN A,BCOUNT ; Get negative byte count
MOVEM A,FILBYT ; Set up number of words to store
MOVE A,NETROT ; Get bit count in first word
MOVE C,NETBYT ; and the bits themselves
SETZ D, ; Nothing in second word
JRST TCRBS3 ; See if can make a byte with what was left
; Here when receive data coroutine returned nothing left
TCRBS2: CALL PUTPAG ; Out of data, send off what we have
JRST SFBSZ ; And finish transfer
; 36-bit binary transfer
TCRB36: SAVEAC <P2> ; Save register for coroutine
XMOVEI P2,T36COR ; Get 36-bit coroutine
DO.
MOVN D,BCOUNT ; Get byte count
MOVEM D,FILBYT ; Count in bytes put
MOVX D,<-1000,,FILPAG> ; Make aobjn pointer to data storage block
JSP P2,(P2) ; Get some data
JRST SFBSZ ; Done, finish up
CALL PUTPAG ; Send them off
LOOP. ; Back for next page
ENDDO.
; Coroutine for 36-bit data
; Call with D/aobjn pointer to words wanted
; returns +1/no more data, +2/success, FILBYT updated for PUTPAG
; will get an integral number of words unless EOF encountered
; Have to be very careful about not trying to read too much as
; paged transfers will signal end with a page type zero and not
; include any data with it.
; Data locations for coroutine:
; A and B are scratch for putting words together
; C contains 36-bit words left in buffer
; D contains AOBJN pointer to data storage area
; P1 contains pointer to buffer
; NETROT saves the coroutine normally found in P1
; NBFPTR points to the next place to read in data
; NETBYT contains max bytes to fill buffer with in left half,
; number of bits left over from fullword in right half
DEFINE T36MAC (SH1,SH2,OFF,%TG1,%TG2) <
LSHC A,^D<SH1> ;; Shift first word into place
MOVE B,OFF(P1) ;; Get word at offset
LSHC A,^D<SH2> ;; Now shift over again
MOVEM A,(D) ;; Store into user space
AOBJN D,%TG1 ;; Count off words returned
JSP P2,1(P2) ;; That was the last, get another request
CALL T36GET ;; Refill buffer
IFSKP.
MOVE B,OFF(P1) ;; Got more, get word again
LSH B,^D<SH2> ;; Shift into place
JRST %TG2 ;; Go on to next word
ENDIF.
HRRZ B,NETBYT ;; Get count of left over bits
IDIV B,FILBSZ ;; Get count of bytes
JUMPE B,T36EOF ;; If none, done now
ADDM B,FILBYT ;; Else count into byte count
MOVE B,OFF(P1) ;; Lost, get last word again
LSH B,^D<SH2> ;; Shift into place
MOVEM B,(D) ;; Store away
JSP P2,1(P2) ;; Return with them
JRST T36EOF ;; Now have EOF
%TG1:! SOJLE C,T36END ;; Count off data, if still left then go on
%TG2:!
>; DEFINE T36MAC
; 36-bit receive coroutine continued
; Here for the actual coroutine
T36COR: MOVEM P1,NETROT ; Save other coroutine
SETZM NETBYT ; No more bytes in buffer
T36FIL: CALL T36GET ; Fill buffer
JRST T36EOF ; No more, done
T36TOP: ADDI P1,9 ; Adjust pointer to top of next frame
MOVE B,0(P1) ; Get first word
T36MAC(32,4,1) ; Put word
T36MAC(28,8,2) ; and another
T36MAC(24,12,3) ; and another
T36MAC(20,16,4) ; and another
T36MAC(16,20,5) ; and another
T36MAC(12,24,6) ; and another
T36MAC(8,28,7) ; and another
T36MAC(4,32,8) ; and another
JRST T36TOP ; Back for more
; Here when ran out of data in middle of transfer
T36END: SKIPN NETBYT ; Was that the real end?
IFSKP.
MOVEM B,(D) ; Yes, save last word
HRRZ B,NETBYT ; Get count of left over bits
IDIV B,FILBSZ ; Get count of bytes
ADDM B,FILBYT ; Count in number of bytes left
ELSE.
CALL T36GET ; Get more data
IFSKP. <JRST T36TOP> ; Got some, go use it
ENDIF.
JSP P2,1(P2) ; Done, return with last batch
T36EOF: JSP P2,0(P2) ; Done with transfer
FATAL <Paged receive coroutine called after EOF>
; Here from T36GET (below) when out out data
T36GTX: MV. ONTBYT,NETBYT ; Here when out of data, restore old NETBYT
MOVE D,OAOBJN ; and AOBJN pointer
EXCH P1,NETROT ; and pointer to input buffer
RET ; and return +1
; 36-bit receive coroutine continued
; Here to refill buffer
T36GET: STKVAR <ONTBYT,OAOBJN,ASKBTS>
MOVEM D,OAOBJN ; Save AOBJN pointer
MV. NETBYT,ONTBYT ; and old NETBYT
;; Calculate A/num bits needed
HLRE A,D ; Get number of words wanted
IMULI A,^D36 ; Turn into number of bits
;; Calculate B/ptr to buffer, C/bits to buffer top
SKIPE C,NETBYT ; Want to start over?
IFSKP.
MOVE B,[POINT 8,NETPAG] ; Yes, point to net page
MOVNI C,37400 ; Can use this many (770 wds, 40 bits/wd)
MOVEI P1,NETPAG-9 ; Set ptr, adjust for ADDI at T36TOP
ELSE.
ADDI A,(C) ; Already have some bits, don't count twice
MOVE B,NBFPTR ; Not starting over, get old pointer
HLRE C,NETBYT ; and number of bits allowed
ENDIF.
;; Check bits needed against buffer top
CAMG C,A ; Is there room?
IFSKP. ; No
SETZM NETBYT ; Won't have anything after this
ELSE. ; Is room
TXZ A,4 ; Make multiple of eight bits wanted
SUB C,A ; Make number of bits we can use after that
HRLZM C,NETBYT ; Save for next time through
MOVE C,A ; Get number of bits we want
ENDIF.
;; Ask lower level coroutine for more data
MOVEM C,ASKBTS ; Save number of bits we ask for
ASH C,-3 ; Turn bits into bytes
EXCH P1,NETROT ; Get data coroutine back
JSP P1,(P1) ; Ask for some data
JRST T36GTX ; None more, lose
EXCH P1,NETROT ; Restore buffer pointer and save coroutine
MOVEM B,NBFPTR ; Save latest pointer to end of buffer
;; Count the data we got back
LSH C,3 ; Get count of bits back
SUB C,ASKBTS ; Form count of bits read in
HRRZ D,ONTBYT ; Get old extra bit count
ADD C,D ; Count them
IDIVI C,^D36 ; Count words
HRRM D,NETBYT ; Save new number of extra bits
MOVE A,C ; Copy word count
IMULI A,^D36 ; Just those bits
IDIV A,FILBSZ ; Number of bytes
ADDM A,FILBYT ; Count off
MOVE D,OAOBJN ; Restore AOBJN pointer
RETSKP ; Return success
; Receive text transfer
TCRTXT: MVI. 5000,BCOUNT ; One page of 5 bytes per word
MVI. 7,FILBSZ ; 7 bits per byte
MVI. <-1,,FILPAG>,FILPTR ; Into where PUTPAG expects it
DO.
MOVE B,FILPTR ; With appropriate pointer
MOVN C,BCOUNT ; With number of bytes
JSP P1,(P1) ; Get some data
JRST SFBSZ ; Done, finish up
MOVEM C,FILBYT ; Save number of bytes for PUTPAG
CALL PUTPAG ; Send them off
LOOP. ; Back for next page
ENDDO.
; Coroutines for network data reception
; call by JSP P1,(P1) with B/byte pointer, C/-count
; may expect F%TEMP preserved over calls but will end with it zero
; return +1/EOF, +2/B and C updated (C all used unless EOF)
; Stream mode
TCRSTR: MOVE A,TCXJFN ; Get network JFN
SIN% ; Read in data
ERJMP TCREOF ; No more, must be done
JSP P1,1(P1) ; Return success
JRST TCRSTR ; And get more
; Here from various modes when nothing left
TCRBBY: DMOVE B,NETPSV ; Get byte pointer and count back
TCREOF: CALL TCXCLS ; Done, close data connection
TCREND: JSP P1,1(P1) ; Return success
JSP P1,0(P1) ; Nothing more
FATAL <TCP receive coroutine called after EOF>
; Here to restore ACs from save block and go signal EOF
TCRNBY: DMOVE B,NETPSV ; Yes, Get ACs back for caller
JRST TCREND ; Go finish up
; Here on error in SIN% when extra bytes needed are counted in NETHDR
TCRHBY: ADD C,NETHDR ; Remember extra bytes in count
JRST TCREOF ; Go return EOF
; Block mode receive
; see RFC765 pages 17-19
; Here on startup of coroutine
TCRBLK: TXZ F,F%TEMP ; Set flag to say no EOF yet
; Drop into...
; Here when we know not EOF, to get another block from the net
TCRBK1: DMOVEM B,NETPSV ; Save pointer and count
SKIPA A,TCXJFN ; Get network JFN
; Skip into...
; Here with saved ACs, read in some more data (A still contains JFN)
; jumped into at TCRBK0 when previous block was worthless - read another
; skipped into from TCRBK1 to avoid EOF check
TCRBK0: TXZN F,F%TEMP ; EOF yet?
SKIPA B,[POINT 8,NETHDR] ; No, point to block header
JRST TCRNBY ; Yes, go handle
MOVNI C,3 ; Three bytes
SIN% ; Read them in
ERJMP TCRBBY ; None more? Signal EOF
;; Now we have header bytes (see RFC765 page 18 for details).
;; Separate the count out and look at the bits in the first byte.
MOVE C,NETHDR ; Get pointer back
LSH C,^D12 ; Shift number into place
ANDI C,177777 ; Keep only number
MOVE B,NETHDR ; Copy header again
TXNE B,1B1 ; EOF?
TXO F,F%TEMP ; Yes, light flag to remember that
JUMPE C,TCRBK0 ; If no bytes, ignore this block
IFXN. B,1B3 ; Restart marker?
MOVE B,[POINT 8,TEMP] ; Yes, point to trash space
MOVNS C ; Negate count
SIN% ; Read and flush
ERJMP TCRBBY ; EOF?
JRST TCRBK0 ; Back for some real data
ENDIF.
MOVNM C,NETHDR ; Save negation of byte count for later
DMOVE B,NETPSV ; Get pointer and requested byte count back
;; Here with ACs normal and negative data count in NETHDR
;; Read until we get a request too big for the rest of this block.
DO.
CAMG C,NETHDR ; Do we want less bytes than available?
EXIT. ; No, need more or equal, go handle specially
EXCH C,NETHDR ; Yes, exchange for subtraction
SUB C,NETHDR ; Count off bytes we will use
EXCH C,NETHDR ; and exchange back
SIN% ; Just read things in
ERJMP TCREOF ; Failed? Assume ran out
JSP P1,1(P1) ; Return success with data
MOVE A,TCXJFN ; Get network JFN again
LOOP. ; Back for more
ENDDO.
;; Requested data too much for remainder of this block
;; Read in last of block and go back for more data.
SUB C,NETHDR ; Get how many extra bytes we will want
EXCH C,NETHDR ; Keep safe for transfer, get block count
SIN% ; Read as much as we know is there
ERJMP TCRHBY ; Lost, go get byte count back
MOVE C,NETHDR ; Get count left
TXZE F,F%TEMP ; If that was the last block
JRST TCREND ; Go return EOF
SKIPN C ; If those were all the bytes we wanted
JSP P1,(P1) ; Get another caller request
JRST TCRBK1 ; Go get more data
; Compressed mode receive
; see RFC765 pages 19-20
; Main entry
TCRCMP: CALL SETPAD ; Set pad char
TXZ F,F%TEMP ; No EOF yet
JRST TCRCP0 ; Go get some
; Here when ran out of filler bytes. Back to very top of loop...
TCRCP5: AOJL C,TCRCP0 ; Count off user space, if want more back now
; JRST TCRCP6 ; Else need to make another request
; Here when finished with user request, ask for more
TCRCP6: TXZE F,F%TEMP ; More to come?
JRST TCREND ; No, that was it
JSP P1,1(P1) ; Yes, more, ask for another request
; JRST TCRCP0 ; And go back with new request
; Here with a new request, read in some data
TCRCP0: DMOVEM B,NETPSV ; Save ACs
MOVE A,TCXJFN ; Get connection JFN
; JRST TCRCP1 ; Check for more data and get it
; First make sure there is more data to get
TCRCP1: TXZE F,F%TEMP ; EOF?
JRST TCREND ; Yes, stop
; JRST TCRCP2 ; Start up reading data
; Read first char of block header
TCRCP2: BIN% ; Read char from it
ERJMP TCRBBY ; End of file, go restore ACs and close
;; Got header byte, see what it is
;; First check if it's the escape byte
IFE. B ; Escape is all zeros. Is that what we have?
BIN% ; Yes, read in following byte
ERJMP TCRBBY ; Lost
TXNE B,100 ; EOF?
TXO F,F%TEMP ; Yes, remember that
TXNN B,20 ; Restart marker?
JRST TCRCP2 ; No, just go back for more
BIN% ; Yes, see what kind
TXZE B,200 ; Top bit lit?
IFSKP.
MOVN C,B ; Yes, get count
MOVE B,[POINT 8,TEMP] ; Point to throwaway buffer
SIN% ; Read trash in
ERJMP TCRBBY ; Lost
JRST TCRCP1 ; Go back for more
ENDIF.
TXNN B,100 ; Filler?
BIN% ; No, replicated byte, flush that
ERJMP TCRBBY ; Lost
JRST TCRCP1 ; Ignore marker and go back for next real data
ENDIF.
;; Next possibility is number of bytes to read
TXZE B,200 ; Top bit lit?
IFSKP. ; No, must be pure string
MOVNM B,NETHDR ; Save number of bytes in string
DMOVE B,NETPSV ; Get pointers back
DO.
CAMG C,NETHDR ; Room for request and more?
EXIT. ; No, don't ask for more
EXCH C,NETHDR ; Have room, switch counts
SUB C,NETHDR ; To count off bytes in how much left
EXCH C,NETHDR ; Now exchange back again
SIN% ; Ask for full amount of user request
ERJMP TCREOF ; Lost, assume end of file
JSP P1,1(P1) ; Get another request
MOVE A,TCXJFN ; Get JFN back again
LOOP. ; Back until we overflow string length
ENDDO.
SUB C,NETHDR ; No room, find out how much will be left over
EXCH C,NETHDR ; Get how much we can get now
SIN% ; Read that much in
ERJMP TCRHBY ; Lost, go finish losing
SKIPE C,NETHDR ; Success, get extra bytes back. If want more
JRST TCRCP0 ; Go get them now
JRST TCRCP6 ; More wanted, go get request
ENDIF.
;; Replicated byte, either padding or given in next byte
;; see which then do it (input pointers are still in NETPSV)
MOVE C,B ; Copy that byte
TXZE C,100 ; Filler?
SKIPA B,PADCHR ; Yes, get padding character
BIN% ; Replicated byte, get which one
ERJMP TCRBBY ; Lost, treat as end of file
HRRM B,NETHDR ; Save fill byte
HRLM C,NETHDR ; And count
DMOVE D,NETPSV ; Get saved caller pointers back
;; Loop dropping that byte into caller space until we run out of reps.
;; If we run out of user pointer, just ask for some more...
;; NETHDR contains count,,byte - first put them in ACs for easy access
DO.
HLRZ A,NETHDR ; Get count of bytes
HRRZ D,NETHDR ; and byte to use
DO.
IDPB D,B ; Drop in
SOJLE A,TCRCP5 ; Count off bytes used
AOJL C,TOP. ; Count off in user space
ENDDO.
HRLM A,NETHDR ; Ran out, save count
JSP P1,1(P1) ; Ask for another input request
LOOP. ; And go back for it
ENDDO.
SUBTTL Sending of data to net
; TCXSND - Send file to net
; takes opened JFN in FILJFN, file properties in FILPRP
; TCPSET and TCXOPN must already have been done
; TCXCLS will be done if necessary
; returns +1/failure, +2/success
TCXSND::SAVEAC <P1> ; Save register to use for coroutine
HLRZ P1,MODCOR ; Get output coroutine to use
CALL SHXINI ; Start typing excls
SETOM INPPAG ; No input page found yet
MOVE A,FILPRP+P.TYPE
JRST @[ TCSBIN ; TT.UNS unspecified - image
TCSTXT ; TT.TXT text
TCSBIN ; TT.BIN binary
TCSPAG ; TT.PAG paged
TCSDIR ; TT.DIR directory
TCSPAG ; TT.MEI other style paged
TCSEBC ; TT.EBC EBCDIC
TCSBIN ](A) ; TT.IMG image (treated as binary)
TCSDIR: TYPE <%% Directory transfers not possible%/>
RET
; Send ASCII data
TCSTXT: MVI. 5000,BCOUNT ; Set number of bytes per page
MVI. <-1,,FILPAG>,FILPTR ; Set pointer to where to get input from file
MVI. <POINT 7,FILPAG>,NBFPTR ; And what to use to output to the net
SETZM NETROT ; No shift of count
; Here to send simple data streams off to net
; BCOUNT/number of file bytes per buffer
; FILPTR/pointer to buffer for file input (must be to FILPAG)
; NBFPTR/pointer to same buffer for net output
; NETROT/how much to shift file byte count to make net byte count
TCSLOP: CALL SETBSZ ; Set file number of bytes
DO.
CALL GETPAG ; Read file page
EXIT. ; That was all
SKIPN C,NBYTES ; Get number of bytes left
EXIT. ; Didn't get any, done
CAMLE C,BCOUNT ; Have more than that?
MOVE C,BCOUNT ; Yes, just use one buffer full
MOVNS C ; Negate for sending
ADDM C,NBYTES ; Count off in total byte count
MOVE B,NETROT ; Get shift count
LSH C,(B) ; Shift word count up to make bytes
MOVE B,NBFPTR ; Get pointer to top of buffer
JSP P1,2(P1) ; Send it off
RET ; Lost
LOOP. ; Sent that page, now go on to the next
ENDDO.
; All data sent, now clean up tail of connection
TCSEND: SETZ C, ; No more data
TCSFIN: JSP P1,0(P1) ; Force last data out and close connection
RET ; Lost
CALLRET SHXUMP ; Won
; Send binary data
TCSBIN: SKIPN B,FILPRP+P.BYTE ; Get byte size
MOVEI B,^D8 ; Eight if not specified
MOVEM B,FILBSZ ; Save
MOVE C,B ; Copy
LSH C,^D24 ; Shift into place
IOR C,[440000,,FILPAG] ; To make byte pointer
MOVEM C,FILPTR ; Save
MOVEI C,^D36 ; Get number of bits in a word
IDIVI C,(B) ; Divide by number of bits in a byte
IMULI C,1000 ; And multiply by number of words in a page
MOVEM C,BCOUNT ; To get number of bytes in a page
JRST @[ REPEAT ^D4,<TCSB36> ; 1 through 4 bits fit into 36
TCSBSH ; 5 bits - shuffle
TCSB36 ; 6 bits fit into 36
TCSBSH ; 7 bits - shuffle
TCSB8 ; 8 bits don't need any shuffling
TCSB36 ; 9 bits fit into 36
TCSBSH ; 10 bits - shuffle
TCSBSH ; 11 bits - shuffle
TCSB36 ; 12 bits fit into 36
REPEAT ^D3,<TCSBSH> ; 13 through 15 bits - shuffle
TCSB8 ; 16 bits don't need any shuffling
TCSBSH ; 17 bits - shuffle
TCSB36 ; 18 bits fit into 36
REPEAT ^D13,<TCSBSH> ; 19 through 31 bits - shuffle
TCSB8 ; 32 bits don't need any shuffling
REPEAT ^D3,<TCSBSH> ; 33 through 35 bits - shuffle
TCSB36 ]-1(B) ; 36 bits fit into 36
; Here for 8, 16, and 32
TCSB8: MVI. <POINT 8,FILPAG>,NBFPTR ; Make pointer for sending data off
JFFO B,.+2 ; Find bit in byte size
FATAL <No bits in 8-bit binary byte size>
MOVNI C,-40(C) ; Get correct count in right half
HRRZM C,NETROT ; Save how much to shift to get 8-bit count
JRST TCSLOP ; Join common code with ASCII mode
; Send binary data with strange byte sizes
; NETROT/number of bits left over, negated
; NETBYT/the bits, right justified
; NETPAG/used as scratch
TCSBSH: SETZM NETROT ; No bits left from previous cycle
CALL SETBSZ ; Set file number of bytes
; Here to get a new page full of data
TCSBST: CALL GETPAG ; Read page full of data
JRST TCSBSX ; That was all
SKIPN C,NBYTES ; Get number of bytes left
JRST TCSBSX ; Didn't get any, done
CAMLE C,BCOUNT ; Have more than that?
MOVE C,BCOUNT ; Yes, just use one buffer full
MOVNS C ; Negate for sending
ADDM C,NBYTES ; Count off in total byte count
IMUL C,FILBSZ ; Turn into number of bits
MOVE D,FILPTR ; Get byte pointer to start
; Here to send the data off
; here with NETROT, NETBYT as above, number of bits and byte pointer in C,D
TCSBSL: ILDB B,D ; Get next byte
ADD C,FILBSZ ; Count off bits
MOVN A,FILBSZ ; Number of bits in it
LSH B,^D36(A) ; Left justify byte
SKIPLE C ; Did we run out of bits?
ADD A,C ; Yes, don't count what we don't have
DMOVEM C,NETPAG ; Save pointer and count
MOVE C,A ; Get number of bits in new byte
MOVE A,NETBYT ; Get old bits left
MOVE D,NETROT ; And how many there were
;; Loop sending quads of 8-bit bytes from the that file byte
;; A,B/bits, C/neg bits in B, D/neg bits in A
DO.
LSHC A,^D32(D) ; Get four bytes in A
ADDI C,^D32(D) ; Count off in file bit count
JUMPG C,ENDLP. ; If not enough for full byte, stop
DMOVEM B,NETPAG+2 ; Save old bits
LSH A,4 ; Left justify new ones
MOVEM A,NETPAG+4 ; Save for coroutine
MOVE B,[POINT 8,NETPAG+4] ; Point to those bytes
MOVNI C,4 ; Four of them
JSP P1,2(P1) ; Send them off
RET ; Lost
DMOVE B,NETPAG+2 ; Get old bits back
SETZ D, ; Nothing in A
LOOP. ; Iterate so we can exit correctly
ENDDO.
;; That byte all sent, get the next
LSH A,(C) ; Right justify remaining bits
MOVEM A,NETBYT ; Save as new remaining bits
MOVNI C,^D32(C) ; Count how many there are
MOVEM C,NETROT ; Count off
DMOVE C,NETPAG ; Get counters back
JUMPG C,TCSBSL ; If have more data, go send it
JRST TCSBST ; No more, get another page
; Here when that was the last of the data
TCSBSX: MOVE A,NETBYT ; Get last bits
MOVE C,NETROT ; Negative of how many there are
LSH A,^D36(C) ; Left justify
MOVEM A,NETBYT ; Save new bits
ASH C,-3 ; Turn number of bits into number of bytes
MOVE B,[POINT 8,NETBYT] ; Point to the bytes we have
CALLRET TCSFIN ; Send last data and return
; Send binary data in integral 36-bit words
TCSB36: MOVEM B,FILBSZ ; Save byte size
SETZM NETROT ; No bits left over
CALL SETBSZ ; Set bytes in file
DO.
CALL GETPAG ; Read file page
EXIT. ; That was all
SKIPN C,NBYTES ; Get number of bytes left
EXIT. ; Didn't get any, done
CAMLE C,BCOUNT ; Have more than that?
MOVE C,BCOUNT ; Yes, just use one buffer full
MOVN D,C ; Negate for sending
ADDM D,NBYTES ; Count off in total byte count
IMUL C,FILBSZ ; Get as number of bits
MOVEI D,FILPAG ; Point to file page again
CALL T36SND ; Send 36-bit data
RET ; Lost
LOOP. ; Sent that page, now go on to the next
ENDDO.
TXO F,F%TEMP ; Need to close connection
CALL T36LST ; Send out last bits of data
RET ; Lost
CALLRET SHXUMP ; Won
; Send paged data
TCSPAG: SETZM NETROT ; No bits left over
DO.
;; Get page of data and information about page
CALL GETPAG ; Get page of file
EXIT. ; That was all, go finish transfer
MVI. 5,HDRLEN ; Five words in header
HRRZM A,PAGNO ; Save page number
;; Count data words in the page
MOVEI D,1000 ; Get how many words we think there are
DO.
SKIPN FILPAG-1(D) ; Anything there?
SOJG D,TOP. ; No, don't send that word
ENDDO.
SKIPG D ; If we ran off the edge of the whole page
MOVEI D,1 ; Send one data word to avoid confusion
MOVEM D,DATLEN ; Save data length in words
;; Always use access controlled pages, set access bits
MVI. PGT$AP,PAGTYP ; Save page type - access controlled page
RPACS% ; Read page access control words
MOVEM B,PAGACS ; Save in header
;; Send the data page off
MOVEI C,5(D) ; Get header and data length
IMULI C,^D36 ; In number of bits
MOVEI D,PAGHDR ; Pointing to top of header
CALL T36SND ; Send words off
RET ; Lost
LOOP. ; Done with that page, on to next
ENDDO.
;; Actual pages all sent, now send FDB and final page together
CALL UNMAP ; Lose last page of file
MVI. 4,HDRLEN ; Four words in this header
SETZM PAGNO ; No page number
MVI. PGT$DP,PAGTYP ; This is a descriptor block
HRRZ A,FILJFN ; With source file
MOVSI B,.FBLEN ; Full FDB starting at the top
MOVEI C,PAGACS ; Into buffer immediately after header words
GTFDB% ; Read FDB words in
MOVE C,PAGACS ; Find next free word - this is FDB size
ANDI C,77
HRROI A,PAGACS(C) ; Now have first string
TXNN KF,K%RDAT ; read date?
SETOM PAGACS+.FBREF ; no, kill it
TXNN KF,K%CDAT ; creation date?
SETOM PAGACS+.FBCRV ; no, kill it
TXNN KF,K%WDAT ; write date?
SETOM PAGACS+.FBCRE ; no, kill both
TXNN KF,K%WDAT
SETOM PAGACS+.FBWRT
MOVX D,<-FDTXTN,,FDTXT> ; Make up the string section
DO.
HLRZ B,(D) ; FDB offset
SKIPGE B,PAGACS(B) ; Get time
IFSKP. ; if we want to pass it
MOVX C,<OT%4YR!OT%TMZ!OT%SCL>
ODTIM%
ERJMP .+1
ELSE.
HRROI B,[ASCIZ /none/] ;if not, generate an error
SETZ C,
SOUT%
ENDIF.
SETZ B, ; make asciz
IDPB B,A
HRROI A,1(A) ; get to next free word
AOBJN D,TOP.
ENDDO.
MOVE B,A ; pointer must now be in B
TXNN KF,K%AUTH
IFSKP.
MOVX D,<-FDTXUN,,FDTXUG>
DO.
HRRZ A,FILJFN ; File
HLL A,(D) ; Function
GFUST%
SETZ A,
IDPB A,B ; make asciz
HRROI B,1(B) ; get to next free word
AOBJN D,TOP.
ENDDO.
ENDIF.
HRRZI C,-<PAGACS>(B) ; number of words sent
MOVEM C,DATLEN ; put it in header
MVI. 4,(B) ; Next block also has four words in header
SETZM 1(B) ; No page number
SETZM 2(B) ; No data
SETZM 3(B) ; This is an EOF page (PGT$LP)
ADDI C,^D8 ; number of words in data plus 2 headers
IMULI C,^D36 ; number of bits
MOVEI D,PAGHDR ; Starting at top of buffer
CALL T36SND ; Send them off
RET ; Propagate fail return
TXZ F,F%TEMP ; Want to keep actual data connection open
CALL T36LST ; Force rest of buffer
RET ; Propagate fail return
CALLRET SHXEND ; Done, return success
FDTXT: XWD .FBCRV,PAGACS+50 ; Times FDB offset,,String variable
XWD .FBWRT,PAGACS+60
XWD .FBREF,PAGACS+70
FDTXTN==.-FDTXT
FDTXUG: XWD .GFAUT,PAGACS+100 ; User name Function code,,String variable
XWD .GFLWR,PAGACS+110
FDTXUN==.-FDTXUG
; Send 36-bit data for binary and paged transfers
; NETROT/count of bits left over from previous time
; NETBYT/bits that were left, right justified
; Call with D/pointer to data, C/number of bits, NETROT/zero first time
; Must always be an integral multiple of 36 bits except the last call
; returns +1/failed, +2/success, all bits sent
T36SND: SAVEAC <P2> ; Get another register
T36SNT: MOVX P2,<-770,,NETPAG> ; Set output pointer
MOVE A,NETBYT ; Get last time's leftover bytes
MOVN B,NETROT ; And how many there were
ADDI C,(B) ; Take into account bits we had left over
LSH B,-2 ; Count nibbles
JRST @[ T36SN0 ; Jump to middle of coroutine
T36SN1
T36SN2
T36SN3
T36SN4
T36SN5
T36SN6
T36SN7
T36SN8 ](B) ; Depending on how many nibbles there were
DEFINE T36SNM (SH,LAB) < ;; Helper macro:
IFN <^D<SH>>,<
MOVE B,(D) ;; Get data word
LSHC A,^D<SH> ;; Shift into place
AOS D ;; Count off word
>
SUBI C,^D32 ;; Sending 32 of them off
JUMPL C,T36SNZ ;; No more, done
LSH A,^D4 ;; Left justify
MOVEM A,(P2) ;; Put away
IFN <^D<SH>>,<LSHC A,^D<36-SH>> ;; Shift rest of bits in
IFE <^D<SH>>,<SETZB A,B> ;; None left, clear out for next cycle
AOBJN P2,LAB ;; Count off in how much we have buffered
IFN <^D<SH>>,<
MOVNI B,^D<36-SH> ;; Get how many of them there are
JRST T36SNB ;; Send buffer out
>
>
T36SN0: T36SNM (32,T36SN1)
T36SN1: T36SNM (28,T36SN2)
T36SN2: T36SNM (24,T36SN3)
T36SN3: T36SNM (20,T36SN4)
T36SN4: T36SNM (16,T36SN5)
T36SN5: T36SNM (12,T36SN6)
T36SN6: T36SNM (8,T36SN7)
T36SN7: T36SNM (4,T36SN8)
T36SN8: T36SNM (0,T36SN0)
; Here when out of buffer in which to put data
; this must be immediately after the T36SNM(0).
T36SNB: MOVEM A,NETBYT ; Save last bits
MOVEM B,NETROT ; and how many there are
ADD C,B ; Don't count twice when we do it again
CALL T36SNS ; Send buffer
RET ; Propagate fail return
JRST T36SNT ; Start it all over again
; Here when out of data to send
T36SNZ: LSH A,(C) ; Shift bits into right justification
ADDI C,^D32 ; Count back bits
MOVEM A,NETBYT ; Save last byte
MOVNM C,NETROT ; Remember how much to use next time
T36SNS: SAVEAC <C,D> ; Enter here for end of buffer routine
MOVNI C,-NETPAG(P2) ; Get count of 32-bit words stuffed
LSH C,2 ; Make into count of 8-bit bytes
MOVE B,[POINT 8,NETPAG] ; Point to the buffer
JSP P1,2(P1) ; Send away
RET ; Lost
RETSKP ; Won
; Here to send off last bit of info
; set F%TEMP to close data conn, clear F%TEMP not to
T36LST: TXZN F,F%TEMP ; Want to close?
AOS P1 ; No, so don't do so
MOVE C,NETROT ; Get count of bits left
MOVE B,NETBYT ; Get the bits
LSH B,^D36(C) ; Shift into left justification
MOVEM B,NETBYT ; Save again
ASH C,-3 ; Turn into number of 8-bit bytes
MOVE B,[POINT 8,NETBYT] ; Pointing to the bits left over
JSP P1,(P1) ; Send off last data
RET ; Lost
RETSKP ; Won
; Send EBCDIC data
TCSEBC: MVI. 5000,BCOUNT ; One page of 5 bytes per word
MVI. <-1,,FILPAG>,FILPTR ; Into where GETPAG expects it
MVI. <POINT 7,FILPAG>,NBFPTR ; And where coroutine can find
CALL SETBSZ ; Set file number of bytes
DO.
CALL GETPAG ; Read file page
JRST TCSEND ; That was all
MOVE A,NBFPTR ; Point to start
CALL TSEBCC ; Send some
RET ; Propagate fail return
CALL TSEBCC ; Send other half
RET ; Propagate fail return
LOOP. ; All done with that page, back for the next.
ENDDO.
; Send a 1.25k-char block of EBCDIC to the net
; call with A/ptr to input, returns +1/fail, +2/success
TSEBCC: SKIPN C,NBYTES ; Get number of bytes left
RETSKP ; None, done
CAILE C,2400 ; Room for them in net buffer?
MOVEI C,2400 ; No, just use what we can take
MOVNS C ; Negate for send count
ADDM C,NBYTES ; Count off in total byte count
MOVE B,[POINT 8,NETPAG] ; Point to buffer
PUSH P,C ; Save count
DO.
ILDB D,A ; Get next 7-bit byte
HLRZ D,EBCTAB(D) ; Convert to EBCDIC
IDPB D,B ; Add to EBCDIC string
AOJL C,TOP. ; Back for more
ENDDO.
MOVE B,[POINT 8,NETPAG] ; Point to buffer again
POP P,C ; Restore count
SAVEAC <A> ; Don't lose byte pointer to source
JSP P1,2(P1) ; Send bytes off
RET ; Lost
RETSKP ; Won
; Coroutines for network data transmission
; call by JSP P1,(P1) with B/byte pointer, C/-count
; call +1/this is the last data to send, +2/last data but keep connection,
; +3/more data to come, so don't do anything special
; C may be zero iff this is the last block (if so nothing sent).
; may use F%TEMP but don't need it preserved (if used will zero it).
; after +1 end call, call again +1 to signal EOF unless page mode.
; return +1/some error, +2/success on all styles of call
; Stream mode write
TCSSTT: JSP P1,1(P1) ; Return success and get another request
TCSSTR: JRST TCSSTC ; Want to push data and close
JRST TCSSTP ; Want to push data but not close
MOVE A,TCXJFN ; Yes, get connection JFN
SKIPE C ; Have anything?
SOUT% ; Yes, send data off
ERJMP TCSERR ; Couldn't
JRST TCSSTT ; Go ask for another request
; Here to push last data, maybe closing connection
TCSSTC: TXOA F,F%TEMP ; Want to close connection
TCSSTP: TXZ F,F%TEMP ; Force data but don't close connection
MOVE A,TCXJFN ; Get connection JFN
SKIPN C ; Have anything?
HRROI B,C ; No, point to a zero since we end on null
SOUTR% ; Send off and push
ERJMP TCSERR ; Couldn't
TXZE F,F%TEMP ; If we wanted to do so,
CALL TCXCLS ; close down data connection
JSP P1,1(P1) ; Return success at closing it
FATAL <TCP send coroutine called after EOF>
; Here on output error
; don't send an ABOR because it is not clear whether or not to wait
; for the 226 reply that might come after a 426 reply.
; Also then I would have to do a complicated sequence of IACs,
; and it was likely that the same error already got to the server anyway.
TCSERR: SKTERS VB.NRM ; Unless being terse
TYPE <%_%% Unexpected error sending data to connection - %J%/>
CALL TCXABT ; Flush losing data connection
JSP P1,0(P1) ; Return failure
FATAL <TCP send coroutine called after error>
; Block mode send
TCSBK0: JSP P1,1(P1) ; Here to return success from previous call
TCSBLK: NOP ; EOF and close, same as not close
TXOA F,F%TEMP ; EOF
TXZ F,F%TEMP ; Normal entry
MOVE A,TCXJFN ; Get connection JFN
DMOVEM B,NETPSV ; Save pointers given us
TXNN F,F%TEMP ; Was that EOF?
TDZA B,B ; No, descriptor is zero
MOVEI B,^D64 ; Yes, use flag signifying EOF
BOUT% ; Send off
MOVN D,NETPSV+1 ; Get positive byte count
MOVE B,[POINT 8,D,23] ; Point to byte count
MOVNI C,2 ; Two bytes
SOUT% ; Send them off
ERJMP TCSERR ; Lost
DMOVE B,NETPSV ; Get pointers back
TXZE F,F%TEMP ; Was that EOF?
JRST TCSSTP ; Yes, send last block off
SOUT% ; No, send off normally
ERJMP TCSERR ; Error, die
JRST TCSBK0 ; Won, go return success
; Compressed mode send
DEFINE TCSCENT (MORE) < ;;Macro to enter TCSCMP coroutine
NOP ;;+1, same as +2
TXOA F,F%TEMP ;;+2, EOF
TXZA F,F%TEMP ;;+3, not EOF
IFSKP. <JUMPE C,MORE> ;; check if there still is data
>
; Main entry point
TCSCMX: JSP P1,1(P1) ; Here if we had no data
TCSCMP: TCSCENT(TCSCMX) ; Set up EOF etc
SAVEAC <P2> ; Need another register
CALL SETPAD ; Set up padding character
IFE. C ; If that was all
MOVE B,[POINT 8,[BYTE (8) 0, ^D64, 300]]
MOVNI C,3 ; Point to this special sequence
MOVE A,TCXJFN ; With data connection
SOUTR% ; Send it off
ERJMP TCSERR
JRST TCSSTP ; Done sending compressed data
ENDIF.
ILDB D,B ; Get next byte
; Here when we have a char (in D) to do something with
TCSCMC: CAME D,PADCHR ; Padding?
JRST TCSCMS ; No, string
; JRST TCSCMF ; Yes, handle it
; Here with D/new padding char
; enter at TCSCFC with NETPSV+1/count already set (e.g. +1 for dupl byte)
TCSCMF: SETZM NETPSV+1 ; No bytes yet
TCSCFC: MOVEM D,NETPSV ; Save new padding char
TCSCFN: AOS NETPSV+1 ; Count byte
SOJG C,TCSCFD ; More?
IFXE. F,F%TEMP ; No, was that EOF?
DO.
JSP P1,1(P1) ; More data expected, get some
TCSCENT(TOP.) ; Reenter coroutine
ENDDO.
JUMPN C,TCSCFD ; If have data now, go use it
ENDIF.
;; Out of data, send final replications off
MOVE B,[POINT 8,[BYTE (8) 0, ^D64, 300]] ; Yes, point to fin seq
CALL TCSCFF ; Get fill seq including EOF marker
JRST TCSSTP ; All done sending
; Compressed mode send, handling padding, ready to read next char
TCSCFD: ILDB D,B ; Get next char
CAMN D,NETPSV ; Same?
JRST TCSCFN ; Yes, count and back again
PUSH P,D ; No, save char
CALL TCSCFF ; Get fill sequence and save ptrs in NETPSV
POP P,D ; Get char back
SOUT% ; Send sequence off
ERJMP TCSERR ; Lost
DMOVE B,NETPSV ; Restore pointers
JRST TCSCMC ; And go handle the new char
; Compressed mode send, make pointers to send pad string off
; mungs D, returns +1/always with A,B,C ready to SOUTR%, old ptrs in NETPSV
TCSCFF: MOVE A,NETPSV ; Get byte
MOVE D,NETPSV+1 ; and how many of it there are
DMOVEM B,NETPSV ; Now save pointers
MOVE B,[POINT 8,NETBUF] ; Get pointer to buffer
SETZ C, ; No bytes put yet
;; First reduce until it will fit in one sequence
DO.
CAIG D,77 ; Got more than one countfull?
EXIT. ; No, ok
SUBI D,77 ; Yes, count off
CAME A,PADCHR ; Pad char?
IFSKP.
MOVEI CX,377 ; Yes, get full pad sequence
IDPB CX,B ; Drop in
SOJA C,TOP. ; Count bytes put and back for next
ENDIF.
MOVEI CX,277 ; Full replicated byte
IDPB CX,B ; Drop in
IDPB A,B ; Drop byte in too
SUBI C,2 ; Two more bytes
LOOP.
ENDDO.
;; Now check for EOF
SKIPN NETPSV+1 ; Did we have any more chars to read?
TXZN F,F%TEMP ; No, was that all?
IFSKP.
MOVEI CX,0 ; Yes, get escape
IDPB CX,B ; Drop in
MOVEI CX,^D64 ; And EOF bit
IDPB CX,B ; Drop that in too
SUBI C,2 ; Count off bits put
ENDIF.
;; Now finish up output buffer with final replicated byte
CAME A,PADCHR ; Fill char?
IFSKP.
TXO D,300 ; Yes, use fill char bits
IDPB D,B ; Drop in
SOS C ; Count
ELSE.
TXO D,200 ; Normal replicated byte, get that
IDPB D,B ; Drop in
IDPB A,B ; With the byte itself
SUBI C,2 ; Count off
ENDIF.
;; Buffer all set, return ready for SOUT%
MOVE A,TCXJFN ; Get JFN
RET ; All done
; Compressed mode send, unreplicated string
TCSCMS: SETZM NETPSV+1 ; No bytes yet
MVI. <POINT 8,NETBUF>,NETPSV ; Init pointer to buffer
TCSCSC: MOVE P2,D ; Get char
SOJG C,TCSCSD ; Count off in input data
IFXE. F,F%TEMP ; Any more expected?
DO.
JSP P1,1(P1) ; Else ask for more data
TCSCENT(TOP.) ; Reenter coroutine
ENDDO.
JUMPN C,TCSCSD ; If more data, go use it
ENDIF.
;; Here on EOF, send string and return
IDPB P2,NETPSV ; Drop last byte in
MOVE B,[POINT 8,[BYTE (8) 0, ^D64, 300]] ; Point to end seq
MOVNI C,2 ; Two bytes
MOVE A,TCXJFN ; To net JFN
SOUT% ; Send off
ERJMP TCSERR ; Lost
AOS B,NETPSV+1 ; Get count
BOUT% ; Send that too
ERJMP TCSERR
MOVN C,NETPSV+1 ; Now get negation of count
MOVE B,[POINT 8,NETBUF] ; and pointer to start of buffer
JRST TCSSTP ; Go finish up sending
; Compressed mode send, unreplicated string continued
; Now we have more data to read, go look at it
TCSCSD: ILDB D,B ; Get next char
CAMN D,PADCHR ; Padding?
JRST TCSCSP ; Yes
CAMN D,P2 ; Same as previous?
JRST TCSCSF ; Yes
IDPB P2,NETPSV ; Not same, drop old byte in
AOS P2,NETPSV+1 ; Get how many bytes left
CAIE P2,177 ; Time to finish this buffer?
JRST TCSCSC ; No, back for the next char
;; Buffer is full, send off before asking for more
DMOVEM B,NETPSV ; Save net pointers
MOVE A,TCXJFN ; Get net JFN
MOVEI B,177 ; Full count
BOUT% ; Send off count
ERJMP TCSERR
MOVE B,[POINT 8,NETBUF] ; Point to buffer
MOVNI C,177 ; Negative count
SOUT% ; Send off string
ERJMP TCSERR
DMOVE B,NETPSV ; Now restore user pointers
JRST TCSCMS ; And go start fresh string
; Here when we have a fill byte, send string and go handle
TCSCSP: IDPB P2,NETPSV ; Yes, put away last char
AOS NETPSV+1 ; And save count
TCSCSF: MOVE P2,NETPSV+1 ; Get count
DMOVEM B,NETPSV ; Save pointers
MOVE A,TCXJFN ; Get net connection
MOVE B,P2 ; And count
BOUT% ; Send off count
ERJMP TCSERR
MOVE B,[POINT 8,NETBUF] ; Point to net buffer
MOVN C,P2 ; Get count negated
SOUT% ; Send string off
ERJMP TCSERR
DMOVE B,NETPSV ; Now get pointers back
CAMN D,PADCHR ; Was that a padding char?
JRST TCSCMF ; Yes, only one of them
MVI. 1,NETPSV+1 ; No, set count for two
JRST TCSCFC ; And go handle duplicated byte
; Setup of padding character for compressed sends and receives
; Note that we use P.XTYP rather than P.TYPE, whereas the rest of the
; code in this module uses P.TYPE. This is because the server is not
; going to know about P.TYPE and this layer should be completely
; transparent. Padding should expand at the remote end as exactly
; the byte it was encoded as here (and vice versa).
SETPAD: SAVEAC <A,B> ; Don't mung registers
MOVE A,FILPRP+P.XTYP ; Get file type remote system is using
SETZ D, ; Assume no pad char
CAIN A,TT.TXT ; For text
MOVEI D," " ; Pad char is space
CAIN A,TT.EBC ; For EBCDIC
HLRZ D,EBCTAB+" " ; Pad char is whatever space converts to
MOVEM D,PADCHR ; Save it
RET
SUBTTL Translation table: ASCII->EBCDIC,,EBCDIC->ASCII
; Stolen from TAPEIO, written by Brian Cox at Stanford GSB, 1979.
; I don't know where he got it from.
; Non-translatables are translated to SUB'S
; Believed to be the same as OPTCD=Q, except:
; ASCII->EBCDIC: ! -> ! (instead of Vert Bar)
; ^ -> Cent (instead of Not)
; Tilde -> Not (instead of Tilde)
; EBCDIC->ASCII: Cent -> ^ (instead of [)
; Not -> Tilde (instead of ^)
; ! > ! (instead of ])
; [ -> [ (instead of Ctrl/Z)
; ] -> ] (instead of Ctrl/Z)
EBCTAB: 0,,0 ; NULL,,NULL
1,,1 ; SOH,,SOH
2,,2 ; STX,,STX
3,,3 ; ETX,,ETX
67,,32 ; EOT,,PF
55,,11 ; ENQ,,HT
56,,32 ; ACK,,LC
57,,177 ; BEL,,DEL
26,,32 ; BS,,GE
5,,32 ; HT,,RLF
45,,32 ; LF,,SMM
13,,13 ; VT,,VT
14,,14 ; FF,,FF
15,,15 ; CR,,CR
16,,16 ; SO,,SO
17,,17 ; SI,,SI
20,,20 ; DLE,,DLE
21,,21 ; DC1,,DC1
22,,22 ; DC2,,DC2
23,,23 ; DC3,,TM
74,,32 ; DC4,,RES
75,,32 ; NAK,,NL
62,,10 ; SYN,,BS
46,,32 ; ETB,,IL
30,,30 ; CAN,,CAN
31,,31 ; EM,,EM
77,,32 ; SUB,,CC
47,,32 ; ESC,,CU1
34,,34 ; FS,,IFS
35,,35 ; GS,,IGS
36,,36 ; RS,,IRS
37,,37 ; US,,IUS
100,,32 ; BLANK,,DS
132,,32 ; !,,SOS
177,,32 ; ",,FS
173,,32 ; #,,
133,,32 ; $,,BYP
154,,12 ; %,,LF
120,,27 ; &,,ETB
175,,33 ; ',,ESC
115,,32 ; (,,
135,,32 ; ),,
134,,32 ; *,,SM
116,,32 ; +,,CU2
153,,32 ; ,,,
140,,5 ; -,,ENQ
113,,6 ; .,,ACK
141,,7 ; /,,BEL
360,,32 ; 0,,
361,,32 ; 1,,
362,,26 ; 2,,SYN
363,,32 ; 3,,
364,,32 ; 4,,PN
365,,32 ; 5,,RS
366,,32 ; 6,,UC
367,,4 ; 7,,EOT
370,,32 ; 8,,
371,,32 ; 9,,
172,,32 ; :,,
136,,32 ; ;,,CU3
114,,24 ; <,,DC4
176,,25 ; =,,NAK
156,,32 ; >,,
157,,32 ; ?,,SUB
174,,40 ; @,,BLANK
301,,32 ; A,,
302,,32 ; B,,
303,,32 ; C,,
304,,32 ; D,,
305,,32 ; E,,
306,,32 ; F,,
307,,32 ; G,,
310,,32 ; H,,
311,,32 ; I,,
321,,136 ; J,,CENT SIGN
322,,56 ; K,,.
323,,74 ; L,,<
324,,50 ; M,,(
325,,53 ; N,,+
326,,41 ; O,,VERTICAL BAR
327,,46 ; P,,&
330,,32 ; Q,,
331,,32 ; R,,
342,,32 ; S,,
343,,32 ; T,,
344,,32 ; U,,
345,,32 ; V,,
346,,32 ; W,,
347,,32 ; X,,
350,,32 ; Y,,
351,,41 ; Z,,!
255,,44 ; [,,$
340,,52 ; \,,*
275,,51 ; ],,)
112,,73 ; ^,,;
155,,176 ; _,,NOT SIGN [136]
171,,55 ; GRAVE,,-
201,,57 ; a,,/
202,,32 ; b,,
203,,32 ; c,,
204,,32 ; d,,
205,,32 ; e,,
206,,32 ; f,,
207,,32 ; g,,
210,,32 ; h,,
211,,32 ; i,,
221,,174 ; j,,|
222,,54 ; k,,,
223,,45 ; l,,%
224,,137 ; m,,_
225,,76 ; n,,>
226,,77 ; o,,?
227,,32 ; p,,
230,,32 ; q,,
231,,32 ; r,,
242,,32 ; s,,
243,,32 ; t,,
244,,32 ; u,,
245,,32 ; v,,
246,,32 ; w,,
247,,32 ; x,,
250,,140 ; y,,GRAVE
251,,72 ; z,,:
300,,43 ; {,,#
152,,100 ; |,,@
320,,47 ; },,'
137,,75 ; TILDE,,=
7,,42 ; DEL,,"
77,,32
77,,141 ; ,,a
77,,142 ; ,,b
77,,143 ; ,,c
77,,144 ; ,,d
77,,145 ; ,,e
77,,146 ; ,,f
77,,147 ; ,,g
77,,150 ; ,,h
77,,151 ; ,,i
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,152 ; ,,j
77,,153 ; ,,k
77,,154 ; ,,l
77,,155 ; ,,m
77,,156 ; ,,n
77,,157 ; ,,o
77,,160 ; ,,p
77,,161 ; ,,q
77,,162 ; ,,r
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,176 ; ,,TILDE
77,,163 ; ,,s
77,,164 ; ,,t
77,,165 ; ,,u
77,,166 ; ,,v
77,,167 ; ,,w
77,,170 ; ,,x
77,,171 ; ,,y
77,,172 ; ,,z
77,,32
77,,32
77,,32
77,,133 ; ,,[
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,135 ; ,,]
77,,32
77,,32
77,,173 ; ,,{
77,,101 ; ,,A
77,,102 ; ,,B
77,,103 ; ,,C
77,,104 ; ,,D
77,,105 ; ,,E
77,,106 ; ,,F
77,,107 ; ,,G
77,,110 ; ,,H
77,,111 ; ,,I
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,175 ; ,,}
77,,112 ; ,,J
77,,113 ; ,,K
77,,114 ; ,,L
77,,115 ; ,,M
77,,116 ; ,,N
77,,117 ; ,,O
77,,120 ; ,,P
77,,121 ; ,,Q
77,,122 ; ,,R
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,134 ; ,,\
77,,32
77,,123 ; ,,S
77,,124 ; ,,T
77,,125 ; ,,U
77,,126 ; ,,V
77,,127 ; ,,W
77,,130 ; ,,X
77,,131 ; ,,Y
77,,132 ; ,,Z
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
77,,60 ; ,,0
77,,61 ; ,,1
77,,62 ; ,,2
77,,63 ; ,,3
77,,64 ; ,,4
77,,65 ; ,,5
77,,66 ; ,,6
77,,67 ; ,,7
77,,70 ; ,,8
77,,71 ; ,,9
77,,32
77,,32
77,,32
77,,32
77,,32
77,,32
END