home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / ddjmag / ddj8606.arc / BPROTO.LST next >
Encoding:
File List  |  1986-06-30  |  26.4 KB  |  1,488 lines

  1. /**
  2.  * Copyright (c) 1985 by Steve Wilhite, Worthington, Ohio
  3.  *
  4.  * Permission is granted to use or distribute this software without any
  5.  * restrictions as long as this entire copyright notice is included intact.
  6.  * You may include it in any software product that you sell for profit.
  7.  *
  8.  * This software is distributed as is, and is not guaranteed to work on any
  9.  * given hardware/software configuration.  Furthermore, no liability is
  10.  * granted with this software.
  11.  *
  12.  * ABSTRACT:
  13.  *
  14.  *    The function, Transfer_File, implements error-free file transfer using
  15.  *    CompuServe's "B" protocol.
  16.  *
  17.  *    It has been assumed that the start-of-packet sequence, DLE "B", has
  18.  *    been detected and the next byte not received yet is the packet
  19.  *    sequence number (an ASCII digit).
  20.  *
  21.  * ENVIRONMENT: Lattice "C", machine independent.
  22.  *
  23.  * AUTHOR: Steve Wilhite, CREATION DATE: 21-Jul-85
  24.  *
  25.  * REVISION HISTORY:
  26.  *
  27.  *    Steve Wilhite, 17-Jan-86
  28.  *    - included a virtual file interface.
  29.  **/
  30.  
  31. /*** Feature Test ***/
  32.  
  33. /* Strip_CR and Strip_LF are mutual exclusive!! */
  34.  
  35. #define Strip_CR    0    /* If true, strip CR's before writing to disk.
  36.                    Add CR before sending */
  37. #define Strip_LF    0    /* If true, strip LD's before writing to disk.
  38.                    Add LF before sending */
  39. /* External Functions */
  40.  
  41. extern Delay();            /* Sleep for "n" milliseconds */
  42. extern Put_Char();        /* Write a character to the display */
  43. extern Start_Timer();        /* Enable the timer for the specified number
  44.                        seconds */
  45. extern int Timer_Expired();    /* Returns "true" if the timer has expired,
  46.                        "false" otherwise */
  47. extern int Wants_To_Abort();    /* Returns "true" if the user wants to abort
  48.                        the file transfer, "false" otherwise */
  49. extern int Read_Modem();    /* Read a character from the comm port.
  50.                        Returns -1 if no character available */
  51. extern int Write_Modem();    /* Send a character to the comm port. Returns
  52.                        "true" is successful, "false" otherwise */
  53.  
  54. /* File I/O Interface */
  55.  
  56. extern int Create_File(), Open_File(), Close_File();
  57. extern int Read_File(), Write_File();
  58.  
  59. #define NUL    0x00
  60. #define ETX    0x03
  61. #define ENQ    0x05
  62. #define DLE    0x10
  63. #define XON    0x11
  64. #define XOFF    0x13
  65. #define NAK    0x15
  66.  
  67. #define True        1
  68. #define False        0
  69. #define Success        -1
  70. #define Failure        0
  71. #define Packet_Size    512
  72. #define Max_Errors    10
  73. #define Max_Time    10
  74. #define Max_Xoff_Time    10
  75. #define WACK        ';'        /* wait acknowledge */
  76.  
  77. /* Sender actions */
  78.  
  79. #define S_Send_Packet    0
  80. #define S_Get_DLE    1
  81. #define S_Get_Num    2
  82. #define S_Get_Seq    3
  83. #define S_Get_Data    4
  84. #define S_Get_Checksum    5
  85. #define S_Timed_Out    6
  86. #define S_Send_NAK    7
  87. #define S_Send_ACK    8
  88.  
  89. /* Receiver actions */
  90.  
  91. #define R_Get_DLE    0
  92. #define R_Get_B        1
  93. #define R_Get_Seq    2
  94. #define R_Get_Data    3
  95. #define R_Get_Checksum    4
  96. #define R_Send_NAK    5
  97. #define R_Send_ACK    6
  98.  
  99. static int
  100.     Ch,
  101.     Checksum,
  102.     Seq_Num,
  103.     R_Size,                /* Size of receiver buffer */
  104.     XOFF_Flag,
  105.     Seen_ETX,
  106.     Seen_ENQ;
  107.  
  108. static char
  109.     S_Buffer[Packet_Size],        /* Sender buffer */
  110.     R_Buffer[Packet_Size];        /* Receiver buffer */
  111.  
  112. static Put_Msg(Text)
  113.     char *Text;
  114.     {
  115.     while (*Text != 0)
  116.     Put_Char(*Text++);
  117.  
  118.     Put_Char('\015');
  119.     Put_Char('\012');
  120.     }
  121.  
  122.  
  123. static Send_Byte(Ch)
  124.     int Ch;
  125.     {
  126.     int TCh;
  127.  
  128.     /* Listen for XOFF from the network */
  129.  
  130.     Start_Timer(Max_Xoff_Time);
  131.     do
  132.     {
  133.     while ((TCh = Read_Modem()) >= 0)
  134.         if (TCh == XON)
  135.         XOFF_Flag = False;
  136.         else if (TCh == XOFF)
  137.             {
  138.         XOFF_Flag = True;
  139.         Start_Timer(Max_Xoff_Time);
  140.         }
  141.     }
  142.     while (XOFF_Flag && !Timer_Expired());
  143.  
  144.     while (!Write_Modem(Ch));
  145.     }
  146.  
  147.  
  148. static Send_Masked_Byte(Ch)
  149.     int Ch;
  150.     {
  151.     /* Mask any protocol or flow characters */
  152.  
  153.     if (Ch == NUL || Ch == ETX || Ch == ENQ || Ch == DLE || Ch == NAK || Ch == XON || Ch == XOFF)
  154.     {
  155.     Send_Byte(DLE);
  156.     Send_Byte(Ch + '@');
  157.     }
  158.     else
  159.     Send_Byte(Ch);
  160.     }
  161.  
  162.  
  163. static Send_ACK()
  164.     {
  165.     Send_Byte(DLE);
  166.     Send_Byte(Seq_Num + '0');
  167.     }
  168.  
  169. static Read_Byte()
  170.     {
  171.     if ((Ch = Read_Modem()) < 0)
  172.     {
  173.     Start_Timer(Max_Time);
  174.  
  175.     do
  176.         {
  177.         if (Timer_Expired())
  178.         return Failure;
  179.         }
  180.     while ((Ch = Read_Modem()) < 0);
  181.     }
  182.  
  183.     return Success;
  184.     }
  185.  
  186.  
  187. static Read_Masked_Byte()
  188.     {
  189.     Seen_ETX = False;
  190.     Seen_ENQ = False;
  191.  
  192.     if (Read_Byte() == Failure)
  193.     return Failure;
  194.  
  195.     if (Ch == DLE)
  196.     {
  197.     if (Read_Byte() == Failure)
  198.         return Failure;
  199.  
  200.     Ch &= 0x1F;
  201.     }
  202.     else if (Ch == ETX)
  203.     Seen_ETX = True;
  204.     else if (Ch == ENQ)
  205.     Seen_ENQ = True;
  206.  
  207.     return Success;
  208.     }
  209.  
  210.  
  211. static Do_Checksum(Ch)
  212.     int Ch;
  213.     {
  214.     Checksum <<= 1;
  215.  
  216.     if (Checksum > 255)
  217.     Checksum = (Checksum & 0xFF) + 1;
  218.  
  219.     Checksum += Ch & 0xFF;
  220.  
  221.     if (Checksum > 255)
  222.     Checksum = (Checksum & 0xFF) + 1;
  223.     }
  224.  
  225. static int Read_Packet(Action)
  226. /**
  227.  * Function:
  228.  *    Receive a packet from the host.
  229.  *
  230.  * Inputs:
  231.  *    Action -- the starting action
  232.  *
  233.  * Outputs:
  234.  *    R_Buffer -- contains the packet just received
  235.  *    R_Size -- length of the packet
  236.  *
  237.  * Returns:
  238.  *    success/failure
  239.  **/
  240.     int Action;
  241.     {
  242.     int
  243.     Errors,
  244.     Next_Seq;
  245.  
  246.     Errors = 0;
  247.  
  248.     while (Errors < Max_Errors)
  249.     switch (Action)
  250.         {
  251.         case R_Get_DLE:
  252.         if (Read_Byte() == Failure)
  253.             Action = R_Send_NAK;
  254.         else if (Ch == DLE)
  255.             Action = R_Get_B;
  256.         else if (Ch == ENQ)
  257.             Action = R_Send_ACK;
  258.  
  259.         break;
  260.  
  261.         case R_Get_B:
  262.         if (Read_Byte() == Failure)
  263.             Action = R_Send_NAK;
  264.         else if (Ch == 'B')
  265.             Action = R_Get_Seq;
  266.         else
  267.             Action = R_Get_DLE;
  268.  
  269.         break;
  270.  
  271.         case R_Get_Seq:
  272.         if (Read_Byte() == Failure)
  273.             Action = R_Send_NAK;
  274.         else
  275.             {
  276.             Checksum = 0;
  277.             Next_Seq = Ch - '0';
  278.             Do_Checksum(Ch);
  279.             R_Size = 0;
  280.             Action = R_Get_Data;
  281.             }
  282.  
  283.         break;
  284.  
  285.         case R_Get_Data:
  286.         if (Read_Masked_Byte() == Failure)
  287.             Action = R_Send_NAK;
  288.         else if (Seen_ETX)
  289.             Action = R_Get_Checksum;
  290.         else if (Seen_ENQ)
  291.             Action = R_Send_ACK;
  292.         else if (R_Size == Packet_Size)
  293.             Action = R_Send_NAK;
  294.         else
  295.             {
  296.             R_Buffer[R_Size++] = Ch;
  297.             Do_Checksum(Ch);
  298.             }
  299.  
  300.         break;
  301.  
  302.         case R_Get_Checksum:
  303.         Do_Checksum(ETX);
  304.  
  305.         if (Read_Masked_Byte() == Failure)
  306.             Action = R_Send_NAK;
  307.         else if (Checksum != Ch)
  308.             Action = R_Send_NAK;
  309.         else if (Next_Seq == Seq_Num)
  310.             Action = R_Send_ACK;    /* Ignore duplicate packet */
  311.         else if (Next_Seq != (Seq_Num + 1) % 10)
  312.             Action = R_Send_NAK;
  313.         else
  314.             {
  315.             Seq_Num = Next_Seq;
  316.             return Success;
  317.             }
  318.  
  319.         break;
  320.  
  321.         case R_Send_NAK:
  322.         Put_Char('-');
  323.         Errors++;
  324.         Send_Byte(NAK);
  325.         Action = R_Get_DLE;
  326.         break;
  327.  
  328.         case R_Send_ACK:
  329.         Send_ACK();
  330.         Action = R_Get_DLE;
  331.         break;
  332.         }
  333.  
  334.     return Failure;
  335.     }
  336.  
  337. static int Send_Packet(Size)
  338. /**
  339.  * Function:
  340.  *    Send the specified packet to the host.
  341.  *
  342.  * Inputs:
  343.  *    Size -- length of the packet
  344.  *    S_Buffer -- the packet to send
  345.  *
  346.  * Outputs:
  347.  *
  348.  * Returns:
  349.  *    success/failure
  350.  **/
  351.     int Size;
  352.     {
  353.     int
  354.     Action,
  355.     Next_Seq,
  356.     RCV_Num,
  357.     I,
  358.     Errors;
  359.  
  360.     Next_Seq = (Seq_Num + 1) % 10;
  361.     Errors = 0;
  362.     Action = S_Send_Packet;
  363.  
  364.     while (Errors < Max_Errors)
  365.     switch (Action)
  366.         {
  367.         case S_Send_Packet:
  368.         Checksum = 0;
  369.         Send_Byte(DLE);
  370.         Send_Byte('B');
  371.         Send_Byte(Next_Seq + '0');
  372.         Do_Checksum(Next_Seq + '0');
  373.  
  374.         for (I = 0; I < Size; I++)
  375.             {
  376.             Send_Masked_Byte(S_Buffer[I]);
  377.             Do_Checksum(S_Buffer[I]);
  378.             }
  379.  
  380.         Send_Byte(ETX);
  381.         Do_Checksum(ETX);
  382.         Send_Masked_Byte(Checksum);
  383.         Action = S_Get_DLE;
  384.         break;
  385.  
  386.         case S_Get_DLE:
  387.         if (Read_Byte() == Failure)
  388.             Action = S_Timed_Out;
  389.         else if (Ch == DLE)
  390.             Action = S_Get_Num;
  391.         else if (Ch == ENQ)
  392.             Action = S_Send_ACK;
  393.         else if (Ch == NAK)
  394.             {
  395.             Errors++;
  396.             Action = S_Send_Packet;
  397.             }
  398.  
  399.         break;
  400.  
  401.         case S_Get_Num:
  402.         if (Read_Byte() == Failure)
  403.             Action = S_Timed_Out;
  404.         else if (Ch >= '0' && Ch <= '9')
  405.             {
  406.             if (Ch == Seq_Num + '0')
  407.             Action = S_Get_DLE;    /* Ignore duplicate ACK */
  408.             else if (Ch == Next_Seq + '0')
  409.             {
  410.             /* Correct sequence number */
  411.  
  412.             Seq_Num = Next_Seq;
  413.             return Success;
  414.             }
  415.             else if (Errors == 0)
  416.             Action = S_Send_Packet;
  417.             else
  418.             Action = S_Get_DLE;
  419.             }
  420.         else if (Ch == WACK)
  421.             {
  422.             Delay(5000);    /* Sleep for 5 seconds */
  423.             Action = S_Get_DLE;
  424.             }
  425.         else if (Ch == 'B')
  426.             Action = S_Get_Seq;
  427.         else
  428.             Action = S_Get_DLE;
  429.  
  430.         break;
  431.  
  432.         case S_Get_Seq:
  433.         /**
  434.          * Start of a "B" protocol packet. The only packet that makes
  435.          * any sense here is a failure packet.
  436.          **/
  437.  
  438.         if (Read_Byte() == Failure)
  439.             Action = S_Send_NAK;
  440.         else
  441.             {
  442.             Checksum = 0;
  443.             RCV_Num = Ch - '0';
  444.             Do_Checksum(Ch);
  445.             I = 0;
  446.             Action = S_Get_Data;
  447.             }
  448.  
  449.         break;
  450.  
  451.         case S_Get_Data:
  452.         if (Read_Masked_Byte() == Failure)
  453.             Action = S_Send_NAK;
  454.         else if (Seen_ETX)
  455.             Action = S_Get_Checksum;
  456.         else if (Seen_ENQ)
  457.             Action = S_Send_ACK;
  458.         else if (I == Packet_Size)
  459.             Action = S_Send_NAK;
  460.         else
  461.             {
  462.             R_Buffer[I++] = Ch;
  463.             Do_Checksum(Ch);
  464.             }
  465.  
  466.         break;
  467.  
  468.         case S_Get_Checksum:
  469.         Do_Checksum(ETX);
  470.  
  471.         if (Read_Masked_Byte() == Failure)
  472.             Action = S_Send_NAK;
  473.         else if (Checksum != Ch)
  474.             Action = S_Send_NAK;
  475.         else if (RCV_Num != (Next_Seq + 1) % 10)
  476.             Action = S_Send_NAK;
  477.         else
  478.             {
  479.             /**
  480.              * Assume the packet is failure packet. It makes no
  481.              * difference since any other type of packet would be
  482.              * invalid anyway. Return failure to caller.
  483.              **/
  484.  
  485.             Errors = Max_Errors;
  486.             }
  487.  
  488.         break;
  489.  
  490.         case S_Timed_Out:
  491.         Errors++;
  492.         Action = S_Get_DLE;
  493.         break;
  494.  
  495.         case S_Send_NAK:
  496.         Put_Char('-');
  497.         Errors++;
  498.         Send_Byte(NAK);
  499.         Action = S_Get_DLE;
  500.         break;
  501.  
  502.         case S_Send_ACK:
  503.         Send_ACK();
  504.         Action = S_Get_DLE;
  505.         break;
  506.         }
  507.  
  508.     return Failure;
  509.     }
  510.  
  511. static Send_Failure(Code)
  512. /**
  513.  * Function:
  514.  *    Send a failure packet to the host.
  515.  *
  516.  * Inputs:
  517.  *    Code -- failure code
  518.  *
  519.  * Outputs:
  520.  *
  521.  * Returns:
  522.  **/
  523.     char Code;
  524.     {
  525.     S_Buffer[0] = 'F';
  526.     S_Buffer[1] = Code;
  527.     Send_Packet(2);
  528.     }
  529.  
  530. static int Receive_File(Name)
  531. /**
  532.  * Function:
  533.  *    Download the specified file from the host.
  534.  *
  535.  * Inputs:
  536.  *    Name -- ptr to the file name string
  537.  *
  538.  * Outputs:
  539.  *
  540.  * Returns:
  541.  *    success/failure
  542.  **/
  543.     char *Name;
  544.     {
  545.     int Data_File;            /* file descriptor */
  546.  
  547.     if ((Data_File = Create_File(Name, 0)) == -1)
  548.     {
  549.     Put_Msg("Cannot create file");
  550.     Send_Failure('E');
  551.     return Failure;
  552.     }
  553.  
  554.     Send_ACK();
  555.  
  556.     for (;;)
  557.     if (Read_Packet(R_Get_DLE) == Success)
  558.         switch (R_Buffer[0])
  559.         {
  560.         case 'N':        /* Data packet */
  561.  
  562.             if (Write_File(Data_File, &R_Buffer[1], R_Size - 1) != R_Size - 1)
  563.             {
  564.             /* Disk write error */
  565.  
  566.             Put_Msg("Disk write error");
  567.             Send_Failure('E');
  568.             Close_File(Data_File);
  569.             return Failure;
  570.             }
  571.  
  572.             if (Wants_To_Abort())
  573.             {
  574.             /* The user wants to kill the transfer */
  575.  
  576.             Send_Failure('A');
  577.             Close_File(Data_File);
  578.             return Failure;
  579.             }
  580.  
  581.             Send_ACK();
  582.             Put_Char('+');
  583.             break;
  584.  
  585.         case 'T':        /* Transfer packet */
  586.  
  587.             if (R_Buffer[1] == 'C') /* Close file */
  588.             {
  589.             Send_ACK();
  590.             Close_File(Data_File);
  591.             return Success;
  592.             }
  593.             else
  594.             {
  595.             /**
  596.              * Unexpected "T" packet. Something is rotten on the
  597.              * other end. Send a failure packet to kill the
  598.              * transfer cleanly.
  599.              **/
  600.  
  601.             Put_Msg("Unexpected packet type");
  602.             Send_Failure('E');
  603.             Close_File(Data_File);
  604.             return Failure;
  605.             }
  606.  
  607.         case 'F':        /* Failure packet */
  608.             Send_ACK();
  609.             Close_File(Data_File);
  610.             return Failure;
  611.         }
  612.     else
  613.         {
  614.         Close_File(Data_File);
  615.         return Failure;
  616.         }
  617.     }
  618.  
  619. static int Send_File(Name)
  620. /**
  621.  * Function:
  622.  *    Send the specified file to the host.
  623.  *
  624.  * Inputs:
  625.  *    Name -- ptr to the file name string
  626.  *
  627.  * Outputs:
  628.  *
  629.  * Returns:
  630.  *    success/failure
  631.  **/
  632.     char *Name;
  633.     {
  634.     int
  635.     Data_File,            /* file descriptor */
  636.     N;
  637.  
  638.     if ((Data_File = Open_File(Name, 0)) == -1)
  639.     {
  640.     Put_Msg("Cannot access that file");
  641.     Send_Failure('E');
  642.     return Failure;
  643.     }
  644.  
  645.     do
  646.     {
  647.     S_Buffer[0] = 'N';
  648.     N = Read_File(Data_File, &S_Buffer[1], Packet_Size - 1);
  649.  
  650.     if (N > 0)
  651.         {
  652.         if (Send_Packet(N + 1) == Failure)
  653.         {
  654.         Close_File(Data_File);
  655.         return Failure;
  656.         }
  657.  
  658.         if (Wants_To_Abort())
  659.         {
  660.         Send_Failure('A');
  661.         Close_File(Data_File);
  662.         return Failure;
  663.         }
  664.  
  665.         Put_Char('+');
  666.         }
  667.     }
  668.     while (N > 0);
  669.  
  670.     if (N == 0)                /* end of file */
  671.     {
  672.     Close_File(Data_File);
  673.     S_Buffer[0] = 'T';
  674.     S_Buffer[1] = 'C';
  675.     return Send_Packet(2);
  676.     }
  677.     else
  678.     {
  679.     Put_Msg("Disk read error");
  680.     Send_Failure('E');
  681.     return Failure;
  682.     }
  683.     }
  684.  
  685. int Transfer_File()
  686. /**
  687.  * Function:
  688.  *    Transfer a file from/to the micro to/from the host.
  689.  *
  690.  * Inputs:
  691.  *
  692.  * Outputs:
  693.  *
  694.  * Returns:
  695.  *    success/failure
  696.  **/
  697.     {
  698.     int I, N;
  699.     char Name[64];            /* holds the file name */
  700.  
  701.     XOFF_Flag = False;
  702.     Seq_Num = 0;
  703.  
  704.     if (Read_Packet(R_Get_Seq) == Success)
  705.     {
  706.     if (R_Buffer[0] == 'T')        /* transfer packet */
  707.         {
  708.         /* Check the direction */
  709.  
  710.         if (R_Buffer[1] != 'D' && R_Buffer[1] != 'U')
  711.         {
  712.         Send_Failure('N');    /* not implemented */
  713.         return Failure;
  714.         }
  715.  
  716.         /* Check the file type */
  717.  
  718.         if (R_Buffer[2] != 'A' && R_Buffer[2] != 'B')
  719.         {
  720.         Send_Failure('N');
  721.         return Failure;
  722.         }
  723.  
  724.         /* Collect the file name */
  725.  
  726.         N = R_Size - 3 > 63 ? 63 : R_Size - 3;
  727.  
  728.         for (I = 0; I < N; I++)
  729.         Name[I] = R_Buffer[I + 3];
  730.  
  731.         Name[I] = 0;
  732.  
  733.         /* Do the transfer */
  734.  
  735.         if (R_Buffer[1] == 'U')
  736.         return Send_File(Name);
  737.         else
  738.         return Receive_File(Name);
  739.         }
  740.     else
  741.         {
  742.         Send_Failure('E');        /* wrong type of packet */
  743.         return Failure;
  744.         }
  745.     }
  746.     else
  747.     return Failure;
  748.     }
  749.     title    Keyboard Driver
  750.     include \lc\dos.mac
  751.     pseg
  752.  
  753.     public    Read_Keyboard
  754.  
  755. Read_Keyboard proc
  756. ;++
  757. ; Function:
  758. ;    Read a "raw" character from the keyboard.
  759. ;
  760. ; Inputs: none
  761. ;
  762. ; Outputs: none
  763. ;
  764. ; Returns:
  765. ;    -1 if no character is available; otherwise a 16-bit code.
  766. ;    If the high byte is zero, then the low byte is an ASCII character,
  767. ;    else the low byte is an "extended" character (scan code).
  768. ;--
  769.     mov    AH,1
  770.     int    16H            ; Scan the keyboard
  771.     jz    Read_Keyboard_1        ; No character available
  772.     mov    AH,0            ; Yes
  773.     int    16H            ; Read keyboard
  774.     cmp    AL,0            ; Extended character
  775.     je    Read_Keyboard_2        ; Yes
  776.     mov    AH,0            ; No, normal character
  777.     ret
  778.  
  779. Read_Keyboard_1:
  780.     mov    AX,-1            ; Denote "no character available"
  781.     ret
  782.  
  783. Read_Keyboard_2:            ; Extended character
  784.     mov    AL,AH
  785.     mov    AH,01H            ; Set the "function key" flags
  786.     ret
  787.  
  788. Read_Keyboard endp
  789.  
  790.     endps
  791.     end
  792. /*
  793.  * This program emulates a dumb terminal with file transfer support using
  794.  * CompuServe's B-Protocol.  This program is just a sample of how to interface
  795.  * the BP module (BP.C) with the rest of the terminal emulator.
  796.  */
  797.  
  798. #define IBM_PC     1
  799.  
  800. extern int Transfer_File(); /* Transfer a file using the "B" protocol */
  801.  
  802. extern int Read_Keyboard(); /* Get a "raw" character from the keyboard */
  803.  
  804. extern Open_Modem();  /* Initialize the comm port */
  805. extern int Read_Modem(); /* Read a character from the comm port */
  806. extern int Write_Modem(); /* Send a character to the comm port */
  807. extern Close_Modem();  /* Release the comm port */
  808.  
  809. #define True  1
  810. #define False  0
  811.  
  812. #define Baud_300 1 /* Baud rate codes used by Open_Modem */
  813. #define Baud_450 2
  814. #define Baud_1200 3
  815. #define Baud_1800 4
  816. #define Baud_2400 5
  817. #define Baud_4800 6
  818. #define Baud_9600 7
  819.  
  820. #ifdef IBM_PC   /* for IBM style keyboards */
  821. #define Exit_Key  0x012D /* Alt-X */
  822. #else
  823. #define Exit_Key  0x001D /* control-] */
  824. #endif
  825.  
  826. #define Is_Function_Key(C)  ((C) > 127)
  827.  
  828. #define ENQ  0x05
  829. #define DLE  0x10
  830. #define ESC  0x1B
  831.  
  832. /*
  833.  * We only support the B-protocol file transfer. No other VIDTEX features.
  834.  */
  835. static char VIDTEX_Response[] = "#DTE,PB,DT\015";
  836.  
  837. static int
  838.     Old_Break_State,
  839.     I,
  840.     Ch,    /* 16-bit "raw" character */
  841.     Want_7_Bit,   /* true if we want to ignore the parity bit */
  842.     ESC_Seq_State;  /* Escape sequence state variable */
  843.  
  844.  
  845. int Wants_To_Abort()
  846. {
  847.     return Read_Keyboard() == ESC;
  848. }
  849.  
  850. main()
  851. {
  852.     char *cp;
  853.  
  854.     Want_7_Bit = True;
  855.     ESC_Seq_State = 0;
  856.  
  857. #ifdef MSDOS
  858.     Old_Break_State = Get_Break();
  859.     Set_Break(0);
  860. #endif
  861.  
  862.     Open_Modem(0, Baud_1200, False);
  863.     puts("[ Terminal Mode ]");
  864.     Ch = Read_Keyboard();
  865.  
  866.     while (Ch != Exit_Key)
  867.  {
  868.  if (Ch > 0)
  869.      {
  870.      if (Is_Function_Key(Ch))
  871.   {
  872.   /* Here to process any local function keys. */
  873.   }
  874.      else
  875.   Write_Modem(Ch & 0x7F);
  876.      }
  877.  
  878.  if ((Ch = Read_Modem()) >= 0)
  879.      {
  880.      if (Want_7_Bit) Ch &= 0x7F;
  881.  
  882.      switch (ESC_Seq_State)
  883.   {
  884.   case 0:
  885.       switch (Ch)
  886.    {
  887.    case ESC:
  888.        ESC_Seq_State = 1;
  889.        break;
  890.  
  891.    case ENQ:
  892.        /* Enquiry -- send ACK for packet 0 */
  893.  
  894.        Write_Modem(DLE);
  895.        Write_Modem('0');
  896.        break;
  897.  
  898.    case DLE:
  899.        ESC_Seq_State = 2;
  900.        break;
  901.  
  902.    default:
  903.        Put_Char(Ch);
  904.    }
  905.  
  906.       break;
  907.  
  908.   case 1:
  909.       /* ESC -- process any escape sequences here */
  910.  
  911.       switch (Ch)
  912.    {
  913.    case 'I':
  914.        /*
  915.         * Reply to the VIDTEX "ESC I" identify sequence
  916.         */        
  917.        cp = VIDTEX_Response;
  918.        while (*cp != 0) Write_Modem(*cp++);
  919.        ESC_Seq_State = 0;
  920.        break;
  921.  
  922.    default:
  923.        Put_Char(ESC);
  924.        Put_Char(Ch);
  925.        ESC_Seq_State = 0;
  926.    }
  927.  
  928.       break;
  929.  
  930.   case 2:
  931.       /* DLE */
  932.  
  933.       if (Ch == 'B')
  934.    {
  935.    /* Start of "B" protocol packet. Go into protocol
  936.     * mode and transfer the file as requested.
  937.     */
  938.  
  939.    if (!Transfer_File()) puts("Transfer failed!");
  940.    }
  941.       else
  942.    {
  943.    Put_Char(DLE);
  944.    Put_Char(Ch);
  945.    }
  946.  
  947.       ESC_Seq_State = 0;
  948.   }
  949.      }
  950.  
  951.  Ch = Read_Keyboard();
  952.  }
  953.  
  954.     Close_Modem();
  955.  
  956. #ifdef MSDOS
  957.     Set_Break(Old_Break_State);
  958. #endif
  959. }
  960.  
  961.     title    Screen
  962.     include    \lc\dos.mac
  963.  
  964. Video        equ    10H        ; IBM BIOS call
  965. TTY_Write    equ    14
  966.  
  967.     pseg
  968.  
  969.     public    Put_Char
  970.  
  971. Put_Char proc
  972. ;++
  973. ; Function:
  974. ;    Write a character to the screen in "normal" TTY-style output.
  975. ;
  976. ; Inputs:
  977. ;    4[BP]    - the character to write
  978. ;
  979. ; Outputs: none
  980. ;
  981. ; Returns: nothing
  982. ;--
  983.     push    BP
  984.     mov    BP,SP
  985.     mov    AL,4[BP]        ; Character to write
  986.     mov    BH,0            ; Current page
  987.     mov    AH,TTY_Write
  988.     int    Video
  989.     pop    BP
  990.     ret
  991. Put_Char endp
  992.  
  993.     endps
  994.     end
  995.  
  996.     include    \lc\dos.mac
  997.  
  998.     dseg
  999. Timer        dw    ?
  1000. Old_Second    db    ?
  1001.     endds
  1002.  
  1003.     pseg
  1004.  
  1005.     public    Start_Timer
  1006.  
  1007. Start_Timer proc
  1008.     push    BP
  1009.     mov    BP,SP
  1010.     mov    AX,[BP+4]        ; Get the number of seconds
  1011.     mov    Timer,AX
  1012.     mov    AX,2C00H
  1013.     int    21H            ; Get current time
  1014.     mov    Old_Second,DH
  1015.     pop    BP
  1016.     ret
  1017. Start_Timer endp
  1018.  
  1019.     public    Timer_Expired
  1020.  
  1021. Timer_Expired proc
  1022.     mov    AX,2C00H
  1023.     int    21H            ; Get current time
  1024.     cmp    DH,Old_Second        ; Has the clock ticked?
  1025.     je    Timer_Expired_1        ; No
  1026.     mov    Old_Second,DH        ; Yes, update Old_Second
  1027.     dec    Timer
  1028.     cmp    Timer,0
  1029.     jle    Timer_Expired_2        ; Timer expired?
  1030.  
  1031. Timer_Expired_1:
  1032.     xor    AX,AX            ; No, return "false"
  1033.     ret
  1034.  
  1035. Timer_Expired_2:
  1036.     mov    AX,1            ; Yes, return "true"
  1037.     ret
  1038. Timer_Expired endp
  1039.  
  1040.     endps
  1041.     end
  1042.  
  1043. #define Loops_Per_Millisecond    9
  1044.  
  1045. Delay(N)
  1046. /**
  1047.  * Delay for N milliseconds
  1048.  **/
  1049.     int N;
  1050. {
  1051.     long K;
  1052.  
  1053.     for (K = Loops_Per_Millisecond * (long) N; K > 0; K--);
  1054. }
  1055.     page    57,132
  1056.     title    FileIO
  1057. ;++
  1058. ; FACILITY: DTE
  1059. ;
  1060. ; ABSTRACT:
  1061. ;
  1062. ;    This module contains the interface routines to the MS-DOS file
  1063. ;    service.  All disk operations are done thru this module.
  1064. ;
  1065. ; ENVIRONMENT: MS-DOS, V2.0 or later
  1066. ;
  1067. ; AUTHOR: Steve Wilhite, CREATION DATE: 8-May-85
  1068. ;
  1069. ; REVISION HISTORY:
  1070. ;
  1071. ;--
  1072.     include    \lc\dos.mac
  1073.     pseg
  1074.  
  1075. @ab = 4                    ; argument base
  1076.     page
  1077.     public    Create_File
  1078.  
  1079. Create_File proc near
  1080. ;+
  1081. ; Functional Description:
  1082. ;
  1083. ;    Creates a new file or truncates an old to zero length in
  1084. ;    preparation for writing.
  1085. ;
  1086. ; Calling Sequence:
  1087. ;
  1088. ;    handle = Create_File(pathname, attribute)
  1089. ;
  1090. ; Parameters:
  1091. ;
  1092. ;    pathname    ptr to ASCIZ pathname
  1093. ;    attribute    file attribute(s)
  1094. ;
  1095. ; Return Value:
  1096. ;
  1097. ;    -5    access denied
  1098. ;    -4    too many open files
  1099. ;    -3    path not found
  1100. ;    else    file handle number
  1101. ;-
  1102. Pathname    equ    @ab[BP]
  1103. Attribute    equ    @ab+2[BP]
  1104.  
  1105.     push    BP
  1106.     mov    BP,SP
  1107.     mov    DX,Pathname
  1108.     mov    CX,Attribute
  1109.     mov    AH,3CH
  1110.     int    21H 
  1111.     jnc    Create_1
  1112.     neg    AX
  1113. Create_1:
  1114.     pop    BP
  1115.     ret
  1116. Create_File endp
  1117.     page
  1118.     public    Open_File
  1119.  
  1120. Open_File proc    near
  1121. ;+
  1122. ; Functional Description:
  1123. ;
  1124. ;    Opens a file.
  1125. ;
  1126. ; Calling Sequence:
  1127. ;
  1128. ;    handle = Open_File(pathname, access)
  1129. ;
  1130. ; Parameters:
  1131. ;
  1132. ;    pathname    ptr to ASCIZ pathname
  1133. ;    access        access code (0 = read, 1 = write, 2 = read and write)
  1134. ;
  1135. ; Return Value:
  1136. ;
  1137. ;    -12    invalid access
  1138. ;    -5    access denied
  1139. ;    -4    too many open files
  1140. ;    -2    file not found
  1141. ;    >0    file handle number
  1142. ;-
  1143. Access        equ    @ab+2[BP]
  1144.  
  1145.     push    BP
  1146.     mov    BP,SP
  1147.     mov    AH,3DH
  1148.     mov    DX,Pathname
  1149.     mov    AL,Access
  1150.     int    21H
  1151.     jnc    Open_1
  1152.     neg    AX
  1153. Open_1:    pop    BP
  1154.     ret
  1155. Open_File endp
  1156.     page
  1157.     public    Close_File
  1158.  
  1159. Close_File proc    near
  1160. ;+
  1161. ; Functional Description:
  1162. ;
  1163. ;    Closes the file associated with a specified file handle.
  1164. ;
  1165. ; Calling Sequence:
  1166. ;
  1167. ;    status = Close_File(handle)
  1168. ;
  1169. ; Parameters:
  1170. ;
  1171. ;    handle        file handle for file to close
  1172. ;
  1173. ; Return Value:
  1174. ;
  1175. ;    -6    invalid handle
  1176. ;    0    no error
  1177. ;-
  1178. Handle        equ    @ab[BP]
  1179.  
  1180.     push    BP
  1181.     mov    BP,SP
  1182.     mov    AH,3EH
  1183.     mov    BX,Handle
  1184.     int    21H
  1185.     jnc    Close_1
  1186.     neg    AX
  1187.     jmp    Close_2
  1188. Close_1:
  1189.     mov    AX,0
  1190. Close_2:
  1191.     pop    BP
  1192.     ret
  1193. Close_File endp
  1194.     page
  1195.     public    Read_File
  1196.  
  1197. Read_File proc    near
  1198. ;+
  1199. ; Functional Description:
  1200. ;
  1201. ;    Transfers a specified number of bytes from a file into a buffer
  1202. ;    location.  If the returned value for number of bytes read is
  1203. ;    zero, then the program tried to read from the end of file.
  1204. ;
  1205. ; Calling Sequence:
  1206. ;
  1207. ;    bytes_read = Read_File(handle, buffer, bytes_to_read)
  1208. ;
  1209. ; Parameters:
  1210. ;
  1211. ;    handle        file handle for the file to read
  1212. ;    buffer        ptr to buffer
  1213. ;    bytes_to_read    number of bytes to read
  1214. ;
  1215. ; Return Value:
  1216. ;
  1217. ;    -6    invalid handle
  1218. ;    -5    access denied
  1219. ;     0    end of file
  1220. ;    >0    number of bytes actually read
  1221. ;-
  1222. Buffer        equ    @ab+2[BP]
  1223. Count        equ    @ab+4[BP]
  1224.  
  1225.     push    BP
  1226.     mov    BP,SP
  1227.     mov    AH,3FH
  1228.     mov    BX,Handle
  1229.     mov    CX,Count
  1230.     mov    DX,Buffer
  1231.     int    21H
  1232.     jnc    Read_1
  1233.     neg    AX
  1234. Read_1:    pop    BP
  1235.     ret
  1236. Read_File endp
  1237.     page
  1238.     public    Write_File
  1239.  
  1240. Write_File proc    near
  1241. ;+
  1242. ; Functional Description:
  1243. ;
  1244. ;    Transfers a specified number of bytes from a buffer into a file.
  1245. ;    If the number of bytes written is not the same as the number
  1246. ;    requested, then an error has occurred.
  1247. ;
  1248. ; Calling Sequence:
  1249. ;
  1250. ;    status = Write_File(handle, buffer, bytes_to_write)
  1251. ;
  1252. ; Parameters:
  1253. ;
  1254. ;    handle        file handle for file to write
  1255. ;    buffer        ptr to buffer
  1256. ;    bytes_to_write    number of bytes to write
  1257. ;
  1258. ; Return Value:
  1259. ;
  1260. ;    -6    invalid handle
  1261. ;    -5    access denied
  1262. ;    else    number of bytes written
  1263. ;-
  1264.       push    BP
  1265.     mov    BP,SP
  1266.     mov    AH,40H
  1267.     mov    BX,Handle
  1268.     mov    CX,Count
  1269.     mov    DX,Buffer
  1270.     int    21H
  1271.     jnc    Write_1
  1272.     neg    AX
  1273. Write_1:
  1274.     pop    BP
  1275.     ret
  1276. Write_File endp
  1277.  
  1278.  
  1279.     public    Move_To_EOF
  1280.  
  1281. Move_To_EOF proc
  1282.     push    BP
  1283.     mov    BP,SP
  1284.     mov    AX,4202H
  1285.     mov    BX,4[BP]        ; file handle
  1286.     xor    CX,CX
  1287.     xor    DX,DX
  1288.     int    21H
  1289.     pop    BP
  1290.     ret
  1291. Move_To_EOF endp
  1292.  
  1293.     endps
  1294.     end
  1295.     Title    Serial
  1296.  
  1297.     include    \lc\dos.mac
  1298.     pseg
  1299. ;+
  1300. ; Table of Contents:
  1301. ;-
  1302. public    Open_Modem
  1303. public    Read_Modem
  1304. public    Write_Modem
  1305. public    Close_Modem
  1306. public    Send_Break
  1307.  
  1308.     dseg
  1309. Comm_Params equ this byte
  1310.     db    11H            ; XON
  1311.     db    13H            ; XOFF
  1312.     db    ?            ; Baud rate code
  1313.     db    0            ; Parity = none
  1314.     db    1            ; Word length = 8
  1315.     db    0            ; stop bits = 1
  1316.     endds
  1317.  
  1318.     extrn    AS_Init:near        ; Initialize
  1319.     extrn    AS_Set_Mode:near    ; Set XON/XOFF mode
  1320.     extrn    AS_Set_Port:near    ; Initialize the port
  1321.     extrn    AS_Open:near        ; Open the port
  1322.     extrn    AS_IReady:near        ; Test input status
  1323.     extrn    AS_IChar:near        ; Input character
  1324.     extrn    AS_OReady:near        ; Test output status
  1325.     extrn    AS_OChar:near        ; Output character
  1326.     extrn    AS_Send_Break:near    ; Send a break signal
  1327.     extrn    AS_OIdle:near        ; Test output idle status
  1328.     extrn    AS_Close:near        ; Close the comm port
  1329.     extrn    AS_Term:near        ; Terminate async I/O
  1330.  
  1331. ;+
  1332. ; Function:
  1333. ;    Open the comm port.
  1334. ;
  1335. ; Calling Sequence:
  1336. ;
  1337. ;    Open_Modem(Port, Rate, Auto_XOFF)
  1338. ;
  1339. ; Parameters:
  1340. ;    Port: 0 = COM1, 1 = COM2
  1341. ;
  1342. ;    Rate:    0    110 baud
  1343. ;             1    300
  1344. ;             2    450
  1345. ;             3    1200
  1346. ;             4    1800
  1347. ;             5    2400
  1348. ;             6    4800
  1349. ;             7    9600
  1350. ;
  1351. ;    Auto_XOFF: if true, enable auto XOFF/XON flow-of-control
  1352. ;-
  1353. Open_Modem proc
  1354.     push    BP
  1355.     mov    BP,SP
  1356.     mov    AX,4[BP]        ; Get port number
  1357.     call    AS_Init            ; Initialize async
  1358.     mov    AX,8[BP]        ; Get XOFF/XON flags
  1359.     cmp    AX,0            ;
  1360.     je    Init_1
  1361.     mov    AX,3
  1362. Init_1:
  1363.     call    AS_Set_Mode        ; Set them
  1364.     mov    AL,6[BP]        ; Get baud rate code
  1365.     mov    Comm_Params[2],AL    ; and store
  1366.     mov    SI,offset Comm_Params
  1367.     call    AS_Set_Port        ; Initialize the port
  1368.     call    AS_Open            ; Open the comm port
  1369.     pop    BP
  1370.     ret
  1371. Open_Modem endp
  1372.  
  1373. ;+
  1374. ; Function:
  1375. ;    Read a character from the comm port.
  1376. ;
  1377. ; Calling Sequence:
  1378. ;
  1379. ;    ret_value = Read_Modem()
  1380. ;
  1381. ; Returns:
  1382. ;    -1 if no character is available; otherwise the character.
  1383. ;-
  1384. Read_Modem proc
  1385.     call    AS_IReady        ; Test input status
  1386.     cmp    AX,-1            ; Ready?
  1387.     jne    Read_1            ; Yes
  1388.     ret                ; No, return -1
  1389.  
  1390. Read_1:
  1391.     call    AS_IChar        ; Input character
  1392.     mov    AH,0
  1393.     ret
  1394. Read_Modem endp
  1395.  
  1396.  
  1397. ;+
  1398. ; Function:
  1399. ;    Write a character to the comm port.
  1400. ;
  1401. ; Calling Sequence:
  1402. ;
  1403. ;    status = Write_Modem(Char)
  1404. ;
  1405. ; Returns:
  1406. ;    0 if could not send the character; otherwise -1
  1407. ;-
  1408. Write_Modem proc
  1409.     push    BP
  1410.     mov    BP,SP
  1411.     call    AS_OReady        ; Test output status
  1412.     not    AX
  1413.     cmp    AX,0            ; Ready?
  1414.     je    Write_1            ; No, return failure    
  1415.     mov    AX,[BP+4]        ; Get character to send
  1416.     call    AS_OChar        ; Send it
  1417.     mov    AX,-1            ; Success
  1418.  
  1419. Write_1:
  1420.     pop    BP
  1421.     ret
  1422. Write_Modem endp
  1423.  
  1424.  
  1425. ;+
  1426. ; Function:
  1427. ;    Close the comm port.
  1428. ;
  1429. ; Calling Sequence:
  1430. ;
  1431. ;    status = Close_Modem()
  1432. ;
  1433. ; Returns:
  1434. ;-
  1435. Close_Modem proc
  1436. Close_1:
  1437.     call    AS_OIdle        ; Test output idle status
  1438.     cmp    AX,0            ; Done?
  1439.     jne    Close_1            ; No
  1440.     call    AS_Close
  1441.     call    AS_Term
  1442.     ret
  1443. Close_Modem endp
  1444.  
  1445. ;+
  1446. ; Function:
  1447. ;    Send a break "character" to the comm port.
  1448. ;
  1449. ; Calling Sequence:
  1450. ;
  1451. ;    Send_Break();
  1452. ;-
  1453. Send_Break proc
  1454.     mov    AX,50            ; milliseconds
  1455.     call    AS_Send_Break
  1456.     ret
  1457. Send_Break endp
  1458.  
  1459.     endps
  1460.     end
  1461.     title    Break
  1462.     include    \lc\dos.mac
  1463.     pseg
  1464.  
  1465.     public    Set_Break, Get_Break
  1466.  
  1467. Set_Break proc
  1468.     push    BP
  1469.     mov    BP,SP
  1470.     mov    DL,4[BP]        ; Get state to set
  1471.     mov    AX,3301H
  1472.     int    21H
  1473.     pop    BP
  1474.     ret
  1475. Set_Break endp
  1476.  
  1477.  
  1478. Get_Break proc
  1479.     mov    AX,3300H
  1480.     int    21H
  1481.     mov    AL,DL
  1482.     xor    AH,AH
  1483.     ret
  1484. Get_Break endp
  1485.  
  1486.     endps
  1487.     end
  1488.