home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / pfkerm.zip / LISTING.KER < prev    next >
Text File  |  1997-05-11  |  32KB  |  490 lines

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