home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / turbopas / ymodem.arc / YMODEM.PAS
Pascal/Delphi Source File  |  1987-10-21  |  10KB  |  409 lines

  1. TITLE: BIXMODEM.INC
  2.  
  3. {                                                             }
  4. {                                                             }
  5. { BIXMODEM.INC  Ymodem procedures for use with BIX.PAS        }
  6. {                                                             }
  7. {                                                             }
  8. {      Program and all Supporting Materials Copyright         }
  9. {      (c) 1985 Barry R. Nance                                }
  10. {               17 Pease Street                               }
  11. {               Wilbraham, Massachusetts 01095                }
  12. {               (413) 596-4031                                }
  13. {                                                             }
  14. {                                                             }
  15.  
  16.  
  17. Var  CRCWork : Integer;
  18.      CRC     : Integer;
  19.  
  20. Function PartialCrc (OldCRC:Integer; C:Char) : Integer;
  21.          {done in 80x8x assembler for speed}
  22. Begin
  23.   CRCWork := OldCRC;
  24.  
  25.   INLINE( $8A / $46 / $04 /        (* Mov     Al,[Bp+4]   *)
  26.           $8B / $1E / CRCWork /    (* Mov     Bx,CRCWork  *)
  27.           $B9 / $08 / $00 /        (* Mov     Cx,8        *)
  28. {Oloop:}  $D0 / $E0 /              (* Shl     Al,1        *)
  29.           $D1 / $D3 /              (* Rcl     Bx,1        *)
  30.           $73 / $04 /              (* Jnc     Iloop       *)
  31.           $81 / $F3 / $21 / $10 /  (* Xor     Bx,$1021    *)
  32. {Iloop:}  $E2 / $F4 /              (* Loop    Oloop       *)
  33.           $89 / $1E / CRCWork )    (* Mov     CRCWork,BX  *);
  34.  
  35.   PartialCRC := CRCWork;
  36.   End;
  37.  
  38.  
  39.  
  40. Procedure ReceiveXMODEM (XName : Str20);
  41. Const
  42.     SOH   = #$01;
  43.     STX   = #$02;
  44.     EOT   = #$04;
  45.     ACK   = #$06;
  46.     NAK   = #$15;
  47.     C_Ch  = 'C';
  48.  
  49.  
  50. Type
  51.     YrecDef     = Array [1..1024] of Char;
  52.     XrecDef     = Array [1..128]  of Char;
  53.  
  54. Var
  55.     Xrec        : XrecDef;
  56.     Yrec        : YrecDef;
  57.     XFile       : File of XrecDef;
  58.  
  59.     XSub        : Integer;
  60.     ErrCnt      : Integer;
  61.     BlockError  : Boolean;
  62.     CurrBlock   : Integer;
  63.     EOTdetected : Boolean;
  64.     BlockLength : Integer;
  65.     Duplicate   : Boolean;
  66.     GetOutFlag  : Boolean;
  67.     FirstNAK    : Boolean;
  68.  
  69.  
  70.  
  71.       Function Abort : Boolean;
  72.       Begin
  73.         Abort := False;
  74.  
  75.         If ErrCnt > 10 then
  76.            Begin
  77.              HighVideo;
  78.              Write (^G);
  79.              Write (
  80.      'Ten errors have occurred on this block.  Continue (Y/N)? ');
  81.              LowVideo;
  82.              Repeat Read(kbd, Key) Until UpCase(Key) in ['N', 'Y'];
  83.              Writeln (Key);
  84.              If UpCase(Key) = 'N' then
  85.                 Begin
  86.                   Abort      := True;
  87.                   GetOutFlag := True;
  88.                   End
  89.              Else
  90.                 ErrCnt := 0;
  91.              End;
  92.  
  93.         End;
  94.  
  95.  
  96.  
  97.  
  98.       Procedure SendNAK;
  99.       Begin
  100.         PurgeBuffer;
  101.  
  102.         If Duplicate then Exit;
  103.  
  104.         SendChar(NAK);
  105.         Writeln ('Requesting re-transmission of block # ', CurrBlock);
  106.         ErrCnt     := Succ(ErrCnt);
  107.         BlockError := True;
  108.         End;
  109.  
  110.  
  111.  
  112.  
  113.       Procedure SendACK;
  114.       Begin
  115.         SendChar(ACK);
  116.         ErrCnt := 0;
  117.         End;
  118.  
  119.  
  120.  
  121.  
  122.       Procedure ReceiveSOH;
  123.       Begin
  124.         ReceiveChar (10, Ch, TimedOut);
  125.  
  126.         If Ch = EOT then
  127.            Begin
  128.              EOTdetected := True;
  129.              SendACK;
  130.              Exit;
  131.              End;
  132.  
  133.         If Ch = C_Ch then
  134.            If CurrBlock = 1 then
  135.               ReceiveChar (10, Ch, TimedOut);
  136.  
  137.         If TimedOut then
  138.            If CurrBlock = 1 then
  139.               If FirstNAK then
  140.                  Begin
  141.                    FirstNAK := False;
  142.                    SendChar (NAK);
  143.                    ReceiveChar (10, Ch, TimedOut);
  144.                    End;
  145.  
  146.         If (TimedOut)
  147.                or
  148.            ((Ch <> SOH) And (Ch <> STX))  then
  149.            Begin
  150.              If TimedOut then
  151.                 Writeln ('Timed out on SOH/STX.')
  152.              Else
  153.                 Writeln ('1st char not SOH/STX.');
  154.              SendNAK;
  155.              End
  156.         Else
  157.             If Ch = STX then
  158.                BlockLength := 1024
  159.             Else
  160.                BlockLength := 128;
  161.         End;
  162.  
  163.  
  164.  
  165.  
  166.       Procedure ReceiveBlockNum;
  167.       Var    Blk     : Byte;
  168.              PrevBlk : Byte;
  169.              FirstCh : Char;
  170.       Begin
  171.         If BlockError then Exit;
  172.  
  173.         Duplicate := False;
  174.         Blk       := CurrBlock Mod 256;
  175.         PrevBlk   := (CurrBlock - 1) Mod 256;
  176.         ReceiveChar (1, Ch, TimedOut);
  177.         FirstCh := Ch;
  178.  
  179.         If (TimedOut) or (Ord(Ch) <> Blk)  then
  180.            If Ord(Ch) <> PrevBlk then
  181.               Begin
  182.                 SendNAK;
  183.                 If TimedOut then
  184.                    Writeln ('Timed out on block number.')
  185.                 Else
  186.                    Writeln ('Block number error (calcd = ', Blk, ').');
  187.                 Exit;
  188.                 End;
  189.  
  190.         ReceiveChar (1, Ch, TimedOut);
  191.         Blk     := 255 - Blk;
  192.         PrevBlk := 255 - PrevBlk;
  193.  
  194.         If (TimedOut) or (Ord(Ch) <> Blk) then
  195.            If Ord(Ch) <> PrevBlk then
  196.               Begin
  197.                 SendNAK;
  198.                 If TimedOut then
  199.                    Writeln ('Timed out on complement.')
  200.                 Else
  201.                    Writeln ('Complement error (calcd = ', Blk, ').');
  202.                 Exit;
  203.                 End;
  204.  
  205.         If Ord(Ch) = PrevBlk then
  206.            If Ord(FirstCh) = CurrBlock Mod 256 then
  207.               Duplicate := True;
  208.  
  209.         End;
  210.  
  211.  
  212.  
  213.  
  214.       Procedure ReceiveDataBlock;
  215.       Begin
  216.         If BlockError then Exit;
  217.         OverrunError := False;
  218.  
  219.  
  220.         Repeat
  221.           XSub := Succ(XSub);
  222.           ReceiveChar (1, Ch, TimedOut);
  223.  
  224.           If Not TimedOut then
  225.              Begin
  226.                Yrec [XSub] := Ch;
  227.                If BlockLength = 1024 then
  228.                   CRC := PartialCRC (CRC, Ch);
  229.                End;
  230.  
  231.           Until (TimedOut) or (XSub = BlockLength) or (OverrunError);
  232.  
  233.  
  234.         If (TimedOut) or (OverrunError) then
  235.            Begin
  236.              SendNAK;
  237.              If TimedOut then
  238.                 Writeln ('Timed out waiting for data.')
  239.              Else
  240.                 Writeln ('Overrun error occurred.');
  241.              OverrunError := False;
  242.              End;
  243.         End;
  244.  
  245.  
  246.  
  247.       Procedure ReceiveCheckSum;
  248.       Var    ChkSum : Byte;
  249.       Begin
  250.         If BlockError then Exit;
  251.         ReceiveChar (1, Ch, TimedOut);
  252.         ChkSum := 0;
  253.         For XSub := 1 to 128 Do
  254.             ChkSum := ChkSum + Ord(Yrec[XSub]);
  255.         If (TimedOut) or (ChkSum <> Ord(Ch)) then
  256.            Begin
  257.              SendNak;
  258.              If TimedOut then
  259.                 Writeln ('Timed out on checksum.')
  260.              Else
  261.                 Writeln (
  262.                 'Checksum error (is ', Ord(Ch), '; should be ', ChkSum, ').');
  263.              End;
  264.         End;
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.       Procedure ReceiveCRC;
  272.       Var
  273.         CRCin  : Integer;
  274.  
  275.       Begin
  276.         If BlockError then Exit;
  277.  
  278.         ReceiveChar (1, Ch, TimedOut);
  279.  
  280.         If Not TimedOut then
  281.            Begin
  282.              CRC   := PartialCRC (CRC, Ch);
  283.              CRCin := ord(Ch) * 256;
  284.              ReceiveChar (1, Ch, TimedOut);
  285.              If Not TimedOut then
  286.                 Begin
  287.                   CRC   := PartialCRC (CRC, Ch);
  288.                   CRCin := CRCin + ord(Ch);
  289.                   End;
  290.              End;
  291.  
  292.         If (TimedOut) or (CRC <> 0) then
  293.            Begin
  294.              SendNAK;
  295.              If TimedOut then
  296.                 Writeln ('Timed out on CRC.')
  297.              Else
  298.                 Writeln (
  299.                 'CRC error (is ', CRCin, '; should be ', CRC, ').');
  300.              End;
  301.         End;
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308. Procedure GetXMODEMBlock;
  309. Begin
  310.   If Keypressed then
  311.      Begin
  312.        GetKey (Key, Extended);
  313.        If Key = Chr(27) then
  314.           Begin
  315.             GetOutFlag := True;
  316.             Exit;
  317.             End;
  318.        End;
  319.  
  320.   BlockError := False;
  321.   ReceiveSOH;
  322.  
  323.   If EOTdetected then Exit;
  324.  
  325.   ReceiveBlockNum;
  326.  
  327.   XSub := 0; CRC := 0;
  328.   ReceiveDataBlock;
  329.  
  330.   If BlockLength = 1024 then
  331.      ReceiveCRC
  332.   Else
  333.      ReceiveCheckSum;
  334.  
  335.   If Not BlockError then
  336.      Begin
  337.        SendACK;
  338.        If Not Duplicate then
  339.           Begin
  340.             Writeln ('Block # ', CurrBlock, ' received.');
  341.             If BlockLength = 128 then
  342.                Begin
  343.                  Move  (Yrec[1], Xrec[1], 128);
  344.                  Write (XFile, Xrec);
  345.                  End
  346.             Else
  347.                Begin
  348.                  For XSub := 1 to 8 Do
  349.                      Begin
  350.                        Move  (Yrec[((XSub - 1) * 128) + 1], Xrec[1], 128);
  351.                        Write (XFile, Xrec);
  352.                        End;
  353.                  End;
  354.             CurrBlock := Succ(CurrBlock);
  355.             End;
  356.        End;
  357.   End;
  358.  
  359.  
  360.  
  361.  
  362.  
  363. Begin                        {of ReceiveXMODEM}
  364.   If XName = '' then Exit;
  365.  
  366.   Assign  (XFile, XName);
  367.   Rewrite (XFile);
  368.  
  369.   Writeln ('File ', XName, ' is being received.');
  370.   Writeln;
  371.  
  372.   UpdateUART (8, 'N', 1);
  373.   PurgeBuffer;
  374.   SendChar(C_Ch);
  375.  
  376.   FirstNAK      := True;
  377.   OverrunError  := False;
  378.   DoingXMODEM   := True;
  379.   XSub          := 0;
  380.   ErrCnt        := 0;
  381.   CurrBlock     := 1;
  382.   BlockError    := False;
  383.   EOTdetected   := False;
  384.   Duplicate     := False;
  385.   GetOutFlag    := False;
  386.  
  387.   Repeat
  388.     GetXMODEMBlock;
  389.     Until (Abort) or (EOTdetected) or (GetOutFlag);
  390.  
  391.   If GetOutFlag then
  392.      Begin
  393.        Close   (XFile);
  394.        Erase   (XFile);
  395.        Writeln ('ERROR--reception of ', XName, ' cancelled.  File erased.');
  396.        End
  397.   Else
  398.      Begin
  399.        Close   (XFile);
  400.        Writeln;
  401.        Writeln (XName, ' successfully received.');
  402.        End;
  403.  
  404.   DoingXMODEM:= False;
  405.   UpdateUART (7, 'E', 1);
  406.  
  407.   End;
  408.  
  409.