home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / pfkerm.zip / PFKERM.TXT < prev   
Text File  |  1997-11-08  |  13KB  |  439 lines

  1. file pfkerm.txt
  2.  
  3.    This is a conversion of the Forth block file KERMIT.DOW
  4.    so as to be readable as a plain text file.  The complete
  5.    package, including the actual block files, is available
  6.    from my web site as
  7.  
  8.          http://www.eskimo.com/~pygmy/pfkerm.zip
  9.  
  10.    This pfkerm.txt and KERMIT.DOW provide the "shadow"
  11. comments to match the source code in the file pfkerm.src
  12. and KERMIT.SCR.
  13.  
  14.  
  15. KERMIT.SCR
  16.   Contains a simple implementation of the Kermit
  17.   file transfer protocol.
  18.  
  19.   copyright 1997 Frank Sergeant               pygmy@pobox.com
  20.                  809 W. San Antonio St.
  21.                  San Marcos, TX  78666
  22.  
  23.   This source code is not Public Domain or Shareware.
  24.   You may use it freely for any private or commercial purpose
  25.   provided you do so at your own risk.
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32. ( load screen    Kermit file transfer protocol)
  33.  
  34. For the algorithm, see pp 98-113 of
  35.  
  36. _C Programmer's Guide to Serial Communications_
  37. by Joe Campbell, Howard W. Sams & Company, 1987,
  38. ISBN 0-672-22584-0.
  39.  
  40. Note, there are some errors in Campbell's examples.
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49. ( KERMIT)
  50.  
  51. GET-Y/N   Wait for the user to press a y, n, Y, or N key.
  52.           Return true if y or Y.  Return false if n or N.
  53.  
  54.  
  55. TRY-AGAIN?
  56.           Display a message and ask whether the user wants
  57.           to try again.  E.g.
  58.           " Drive not ready" TRY-AGAIN? IF ... THEN
  59.  
  60. .MSG      clears the top 2 lines of the screen and displays a
  61.           message, leaving the cursor positioned just past the
  62.           message.  E.g.  " Starting the transfer ..." .MSG
  63.  
  64.  
  65.  
  66. ( KERMIT)
  67. MYMAXL maximum "len" we are willing to handle.
  68.        The transmitted LEN field includes SEQ, TYPE, DATA, CKSUM
  69.        fields.  94 maximum allowed under basic Kermit.  Our
  70.        buffers must be 1 byte longer to hold the LEN field also.
  71. OUT-BUF & IN-BUF buffers for building outgoing or receiving
  72.        incoming frames.  We store LEN, SEQ, TYPE, DATA, CKSUM
  73.        fields, but not the SOH nor the ending CR.
  74. OUTLEN & INLEN count bytes currently in the buffers
  75. MAXL   holds agreed-upon maximum "len" value, which is
  76.        the MIN of receiver's and sender's preferences.
  77.  
  78. a "character-ized" number is produced by adding a "space."  The
  79. result must be <= $7E, thus the original number must be
  80. <= $5E (ie 94 decimal).
  81.  
  82.  
  83. ( KERMIT)
  84.  
  85. MAXL, QCTL, etc are the agreed-upon protocol parameters for
  86. the session.  INIT-LIMITS initializes these to the values
  87. we would prefer to use.  The sender and receiver exchange
  88. an S-frame and an ACK-frame listing their preferences.  We
  89. then compromise by taking the MIN between them.
  90.  
  91.  
  92.  
  93.  
  94.  
  95.  
  96.  
  97.  
  98.  
  99.  
  100. ( KERMIT)
  101.  
  102. We make >LEN, >TYPE, etc relative to the start of the buffer
  103. so we can use the same definitions for both the receiving and
  104. sending buffers.  >CKSUM assumes the LEN byte has been
  105. initialized.
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117. ( KERMIT  - compromise on the parameters)
  118.  
  119. COMPROMISE assumes we have an S frame in one buffer and its
  120. ACK frame in the other buffer.  We don't care whether we are
  121. the sender or receiver.  The compromise takes the more
  122. conservative setting from each buffer as the actual protocol
  123. parameter to use.
  124.  
  125. For now, we will ignore all the settings except for MAXL and
  126. TIMEOUT, taking the MIN of MAXL and the MAX of TIMEOUT.
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134. MYMENU    cheap error handling in the case where the user
  135.           chooses to abort the file transfer process.  Set up
  136.           your own menu ( ' MYREALMENU IS MYMENU ) or allow the
  137.           default 'no vector' error to occur.
  138.  
  139. KSER-IN   gets a serial character and tests whether it is SOH,
  140.           all the while checking for a time-out.  Returns
  141.           character and SOH-flag (true if character is SOH).
  142.           In case of time out, return up an extra level,
  143.           putting a 'V on the stack as the dummy frame type
  144.           indicating a time out followed by a true flag
  145.           indicating a 'good' check sum.
  146.           Note, KSER-IN is only called by GETFRAME and so is
  147.           always called with the correct stack depth.  To test
  148.           it standalone, nest it once in a test word, as shown
  149.           in TEST-IN.
  150.  
  151. ( KERMIT)
  152. We "controlify" a control code (0-$1F, $7F) by flipping bit 6
  153. and preceding it with the QCTL character (usually '#).  The
  154. QCTL character itself is escaped.  We count QCTL as a control
  155. character in CTRL? so we can escape it, but we only flip bit
  156. 6 for actual control characters.  Also, consider $7E (~) to
  157. be a control character, as it is used for repeat counts
  158.  
  159. (KEMIT puts a character into OUT-BUF and increments the count
  160. KEMIT writes a character into OUT-BUF, escaping it if necessary.
  161. ROOM? says whether there is room in the buffer for another
  162.       character.  We require 2 bytes available in case the
  163.       next character needs to be escaped.  If we allowed
  164.       high-bit escpaping we would require 3 bytes instead.
  165.  
  166.  
  167.  
  168. ( KERMIT)
  169. CK%%  converts the raw checksum of all the bytes
  170.       after SOH into a checksum character by wrapping
  171.       and characterifying it according to the KERMIT algorithm.
  172.  
  173. CKSUM  calculates a checksum on a buffer by adding the bytes
  174.        in the LEN SEQ TYPE & DATA fields and applying CK%%.
  175.        The LEN field must include the cksum byte.
  176.  
  177. CKSUM? Calculate the checksum character for the input frame
  178.        and compare it to the transmitted checksum character.
  179.        Return true if the checksum is good.
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186. MODEM! sends a character to the modem.  We defer it to make
  187.       testing easy.
  188.  
  189. DATA! builds an entire data field, stopping either when out
  190.       of source characters or out of room in OUT-BUF.
  191.  
  192. BUILD-FRAME  Given the address and length of data to be
  193.       transferred and the type of the frame, put as much of
  194.       the data as will fit into a frame and return the address
  195.       and length of the remaining (i.e. unsent) data.
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202. ( KERMIT - debugging aids)
  203.  
  204. .FRAME .INB .OUTB  are used for testing to dump the contents
  205.                    of the buffers to the screen.
  206.  
  207. TEST1 TEST2  provide some test data
  208.  
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.  
  216.  
  217.  
  218.  
  219. ( KERMIT)
  220.  
  221. SENDFRAME sends an entire header, from SOH through 1-byte
  222.       checksum and ending carriage return, to the "modem."
  223.       It sends SOH, sends LEN+1 characters in the OUT-BUF,
  224.       and then sends a carriage return.
  225.  
  226.  
  227.  
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.  
  236. ( KERMIT)
  237.  
  238. LIMITS provides data for use in building either an S-frame
  239.        or its ACK frame for purposes of negotiating
  240.        the protocol as to maximum frame length, etc.
  241.        Note that PADC is controlified, but seems not to
  242.        be "escaped" -- after all, we haven't agreed upon
  243.        the escape character at the time of sending the
  244.        S-frame.  We build this frame directly into OUT-BUF
  245.        to prevent DATA! from escaping any characters.
  246.        We say we'll use (~) as the repeat character, but we
  247.        will _not_ use repeat counts when we transmit, but we
  248.        _will_ handle them when we receive.  If the sender does
  249.        not escape actual tildes, then we will have a problem.
  250.  
  251.  
  252.  
  253. ( KERMIT)
  254. KINIT sends the 'send-init' frame.  It must have sequence zero.
  255.       This is the 'S' frame sent by sender in response to the
  256.       receiver's initial NAKs.
  257. KINITACK sends a reply to a 'send-init' frame.  Before sending
  258.       KINITACK (if we are receiving) or after receiving
  259.       KINITACK (if we are sending), we must adjust our settings
  260.       to the minimum of the sender's and the receiver's requests
  261.       Note complex handling of COMPROMISE.
  262. FILEHEADER sends the file name of the file to be transmitted.
  263.  
  264. EOF is sent at the end of each file we send.  EOT is sent after
  265. we finish sending all the files.  Reciever sends ACK or NAK
  266. after each frame is received, depending on whether chksum is
  267. ok.  ERROR is sent to abandon the session.  I think we will
  268. ignore an ATTRIB frame.
  269.  
  270. ( KERMIT)
  271.  
  272. EXPECTED   holds the count of bytes we expect to receive
  273.            following the length byte.
  274.  
  275.  
  276. SETLENGTH  handles the length count for an incoming frame,
  277.            initializing EXPECTED and INLEN and putting the
  278.            length byte into the input buffer.
  279.  
  280. PUT-IN-BUFFER  puts input bytes into the buffer and returns
  281.                a flag that is true when all the expected bytes
  282.                have arrived.
  283.  
  284.  
  285.  
  286.  
  287.  
  288. GETFRAME is closely tied to KSER-IN and is the only word that
  289.  should ever call KSER-IN, as KSER-IN returns upward an extra
  290.  level in case of a timeout, supplying the type and cksum flag
  291.  (ie 'V -1).  So, GETFRAME always succeeds, returning a type
  292.  and flag.  It watches for an SOH in the middle of a frame and
  293.  starts over.  What makes GETFRAME tricky is it needs to handle
  294.  the usual case as well as a timeout at any time as well as an
  295.  unexpected SOH at any time.  What makes it simpler is pushing
  296.  some of the logic down to the word KSER-IN and letting KSER-IN
  297.  terminate not only itself but also GETFRAME in the case of a
  298.  timeout, thus producing a dummy V-frame.  After that we no
  299.  longer have a timeout as a special case, we simply have an
  300.  additional "frame" type (i.e. a timeout frame).
  301.  
  302.  
  303.  
  304. ( KERMIT)
  305.  
  306. GET-GOOD-FRAME  continues to try to get a frame until one
  307.                 arrives with a good checksum.  It will try
  308.                 forever unless the user aborts the transfer.
  309.                 (See KSER-IN for test for user abort.)
  310.  
  311.  
  312. IN-SEQ     sequence number of the frame in the input buffer
  313.  
  314. GOOD-SEQ?  true if the input frame's sequence number is the
  315.            expected sequence number.
  316.  
  317.  
  318.  
  319.  
  320.  
  321. ( KERMIT)
  322.  
  323. (GETACK keeps getting frames until one comes in with a good
  324.         checksum.  V-frames are ok.
  325.  
  326. GETACK keeps getting ack frames, handling or ignoring each, as
  327.        appropriate.  It re-sends the data frame in case of a
  328.        V-frame (timeout) or a NAK with the correct sequence
  329.        number.  It is used only by the sender.  Later, it
  330.        could bail out if too many NAKs or timeouts occur in a
  331.        row, etc.
  332.  
  333. READ  load up the buffer from the file in preparation for
  334.       transmitting it via the serial port
  335.  
  336.  
  337.  
  338. ( KERMIT)
  339.  
  340. GET-FIRST-NAK ignores timeouts and sequence numbers and waits
  341.       for a NAK from the receiver.
  342.  
  343. SEND  wait for the prompting NAK frame from receiver
  344.       send S-frame ( ie KINIT)
  345.       reset serial in to throw away any extra prompting NAKs
  346.       get S-frame ACK for SEQ 0
  347.       send the entire file, one D-frame at a time
  348.       close the file
  349.       send end of file and end of transmission
  350.  
  351.  
  352.  
  353.  
  354.  
  355. ( KERMIT)
  356.  
  357. IN-DATA  is a buffer for holding the UNCTRL'd data field.  Make
  358.          it big in case lots of repeat counts are present.
  359.  
  360. C!+ stores a character and bumps the address (similar to C@+)
  361.  
  362. C@+-  gets a character from the 'from' address, increments
  363.       the 'from' address and decrements the count of remaining
  364.       characters.
  365.  
  366. UNCTRL'd if the current character is the QCTL escape character,
  367.          get another character and unescape it.
  368.  
  369.  
  370.  
  371.  
  372.  
  373. REPEAT'd The most recent character was the tilde (~), indicating
  374.          the beginning of a 3 or 4 character repeat sequence.
  375.          Get the next character as the count and then the next 1
  376.          or 2 (if escaped) to find the value to be repeated, &
  377.          expand that repeated character into destination buffer.
  378.  
  379. UNCTRL copy the escaped and repeated source buffer,
  380.        unescaping and expanding as appropriate, to the
  381.        destination buffer.
  382.  
  383. >IN-DATA  copies IN-BUF's data field, which may contain
  384.           escaped characters, to IN-DATA with escaped characters
  385.           converted to their actual values (and repeated counts
  386.           expanded).
  387.  
  388.  
  389. ( KERMIT)
  390.  
  391. BUILDFNAME extracts name of file to be received from an
  392.            input F frame and stores it in our KFNAME buffer
  393.            as a counted string (and an asciiz string suitable
  394.            for passing to DOS for creating the file).
  395.  
  396. RCVNAME  this is what we do in response to an F-frame:
  397.          save the file name in the KFNAME buffer as
  398.          a counted, asciiz string, then create the file and
  399.          save the handle.
  400.  
  401.  
  402.  
  403.  
  404.  
  405.  
  406. ( KERMIT)
  407.  
  408. GET-NEXT  Get the next frame we are expecting, ACKing or NAKing
  409.           as appropriate.
  410.           Always ack with the seq number we received, even if
  411.           it wasn't the seq number we expected, thus allowing
  412.           sender to continue.  But, throw away frames that
  413.           do not have the expected seq number.  Except, if
  414.           V-frame (ie timeout) or if a bad checksum, then
  415.           NAK with our expected sequence number.
  416.           It is possible a D-frame should not be ACK'd until
  417.           after we have written it to disk, in case disk writes
  418.           interfere with servicing the serial port.
  419.  
  420. WRITE     Append input data to the file.
  421.  
  422.  
  423. ( KERMIT)
  424.  
  425. RECEIVE  send NAK every second until we see SOH, then get
  426.          the rest of that first frame -- until we get the
  427.          S-frame.  Then compromise on settings and send
  428.          an ack for the S-frame.  Then, handle the frame
  429.          types, getting file name and opening it for an
  430.          F-frame, writing D-frames to the file, closing
  431.          the file upon getting a Z-frame, and exiting upon
  432.          getting a B-frame (EOT).
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.