home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / PASCAL / MISCTI10.ZIP / TI407.ASC < prev    next >
Encoding:
Text File  |  1988-05-02  |  21.6 KB  |  579 lines

  1. { This  version  of Michael Quinlan's ASYNC.INC is compatible 
  2. with IBM  PC  and  Compatibles.  It  gives  interrupt-driven 
  3. buffered communication capabilities  to Turbo Pascal  programs
  4. written for the IBM PC. It is heavily dependent on that hardware.
  5.  
  6. The Async_ITR routine was taken from N. Arley Dealey's Async4
  7. procedures, to make this set of routines work with version 4.0 of
  8. Turbo Pascal.
  9.  
  10. The  following example routines are public domain  programs  that
  11. have  been uploaded to our Forum on CompuServe.  As a courtesy to
  12. our  users  that  do not have  immediate  access  to  CompuServe,
  13. Technical Support distributes these routines free of charge. 
  14.  
  15. However,  because these routines are public domain programs,  not
  16. developed by Borland International,  we are unable to provide any
  17. technical support or assistance using these routines. If you need
  18. assistance   using   these   routines,    or   are   experiencing
  19. difficulties,  we  recommend  that you log  onto  CompuServe  and
  20. request  assistance  from the Forum members that developed  these
  21. routines.
  22. }
  23.  
  24. Unit Async;
  25.  
  26. Interface
  27.  
  28. Uses DOS;
  29. {--------------------------------------------------------------}
  30. {                        ASYNC.INC                             }
  31. {                                                              }
  32. {  Async Communication Routines                                }
  33. {  by Michael Quinlan                                          }
  34. {  with a bug fixed by Scott Herr                              }
  35. {  with Async_ISR update to 4.0 by N. Arley Dealey substituted }
  36. {                               by Keith Hawes                 }
  37. {  made PCjr-compatible by W. M. Miller                        }
  38. {  Highly dependent on the IBM PC and PC DOS 2.0 or later      }
  39. {                                                              }
  40. {  based on the DUMBTERM program by CJ Dunford                 }
  41. {  in the January 1984                                         }
  42. {  issue of PC Tech Journal.                                   }
  43. {                                                              }
  44. {  Entry points:                                               }
  45. {--------------------------------------------------------------}
  46.  
  47. Procedure Async_Init;
  48. {--------------------------------------------------------------}
  49. {      Performs initialization.                                }
  50. {                                                              }
  51. {--------------------------------------------------------------}
  52.  
  53. function Async_Open(ComPort       : Word;
  54.                     BaudRate      : Word;
  55.                     Parity        : Char;
  56.                     WordSize      : Word;
  57.                     StopBits      : Word) : Boolean;
  58. {--------------------------------------------------------------}
  59. {   Sets up interrupt vector, initialize the COM port for      }
  60. {   processing, sets pointers to the buffer.  Returns FALSE    }
  61. {   if COM port not installed.                                 }
  62. {--------------------------------------------------------------}
  63.  
  64. Function Async_Buffer_Check(var C : Char) : Boolean;
  65. {--------------------------------------------------------------}
  66. {      If a character is available, returns TRUE and moves the }
  67. {        character from the buffer to the parameter            }
  68. {      Otherwise, returns FALSE                                }
  69. {--------------------------------------------------------------}
  70.  
  71. Procedure Async_Send(C : Char);
  72. {--------------------------------------------------------------}
  73. {      Transmits the character.                                }
  74. {--------------------------------------------------------------}
  75.  
  76. Procedure Async_Send_String(S : string);
  77. {--------------------------------------------------------------}
  78. {      Calls Async_Send to send each character of S.           }
  79. {--------------------------------------------------------------}
  80.  
  81. Procedure Async_Close;
  82. {--------------------------------------------------------------}
  83. {    Turns off the COM port interrupts.                        }
  84. {    ** MUST ** BE CALLED BEFORE EXITING YOUR PROGRAM;         }
  85. {    otherwise you will see some really strange errors and     }
  86. {    to re-boot.                                               }
  87. {--------------------------------------------------------------}
  88.  
  89. procedure Async_Change(BaudRate      : Word;
  90.                        Parity        : Char;
  91.                        WordSize      : Word;
  92.                        StopBits      : Word);
  93. {--------------------------------------------------------------}
  94. { Changes communication parameters "on the fly".               }
  95. { You cannot use the BIOS routines because they drop DTR.      }
  96. {--------------------------------------------------------------}
  97.  
  98. var
  99.   Async_Buffer_Overflow : Boolean;
  100.                         { True if buffer overflow has happened }
  101.   Async_Buffer_Used     : Word;
  102.   Async_MaxBufferUsed   : Word;
  103.  
  104. Implementation
  105.                                          { global declarations }
  106.  
  107. const
  108.   UART_THR = $00;
  109.                { offset from base of UART Registers for IBM PC }
  110.   UART_RBR = $00;
  111.   UART_IER = $01;
  112.   UART_IIR = $02;
  113.   UART_LCR = $03;
  114.   UART_MCR = $04;
  115.   UART_LSR = $05;
  116.   UART_MSR = $06;
  117.  
  118.   I8088_IMR = $21;
  119.                  { port address of the Interrupt Mask Register }
  120.  
  121. const
  122.   Async_Buffer_Max     = 4095;
  123. var
  124.   Async_Interrupt_Save : pointer;
  125.   Async_ExitProc_Save  : pointer;
  126.   Async_Buffer         : Array[0..Async_Buffer_Max] of char;
  127.   Async_Open_Flag      : Boolean;
  128.   Async_Port           : Word; { current Open port number     }
  129.                                { (1 or 2)                     }
  130.   Async_Base           : Word; { base for current open port   }
  131.   Async_Irq            : Word; { irq for current open port    }
  132.  
  133.                        { Async_Buffer is empty if Head = Tail }
  134.  
  135.   Async_Buffer_Head    : Word;   { Locn in Async_Buffer to put }
  136.                                   { next char                  }
  137.   Async_Buffer_Tail    : Word;    { Locn in Async_Buffer to get}
  138.                                   { next char                  }
  139.   Async_Buffer_NewTail  : Word;
  140.  
  141.   Async_BIOS_Port_Table : Array[1..2] of Word absolute $40:0;
  142.     { This table is initialized by BIOS equipment determination}
  143.     { code at boot time to contain the base addresses for the  }
  144.     { installed async adapters.  A value of 0 means "not in-   }
  145.     { stalled."                                                }
  146.  
  147. const
  148.   Async_Num_Bauds = 8;
  149.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  150.                                          Baud, Bits : Word
  151.                                       end
  152.                    = ((Baud:110;  Bits:$00),
  153.                       (Baud:150;  Bits:$20),
  154.                       (Baud:300;  Bits:$40),
  155.                       (Baud:600;  Bits:$60),
  156.                       (Baud:1200; Bits:$80),
  157.                       (Baud:2400; Bits:$A0),
  158.                       (Baud:4800; Bits:$C0),
  159.                       (Baud:9600; Bits:$E0));
  160.  
  161. procedure BIOS_RS232_Init(ComPort, ComParm : Word);
  162.  
  163. { Issue Interrupt $14 to initialize the UART   }
  164. { Format of ComParm:  (From IBM Tech. Ref.)    }
  165. {                                              }
  166. { 7     6     5     4     3     2      1     0 }
  167. { --Baud Rate--     -Parity   StopBit  Word Len}
  168. {  000 =  110       x0 = None   0 = 1  10 = 7  }
  169. {  001 =  150       01 = Odd    1 = 2  11 = 8  }
  170. {  010 =  300       11 = Even                  }
  171. {  011 =  600                                  }
  172. {  100 = 1200                                  }
  173. {  101 = 2400                                  }
  174. {  110 = 4800                                  }
  175. {  111 = 9600                                  }
  176. {                                              }
  177.  
  178. var
  179.   Regs : registers;
  180. begin
  181.   with Regs do
  182.     begin
  183.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  184.       dx := ComPort;
  185.       Intr($14, Regs)
  186.     end;
  187. end; { BIOS_RS232_Init }
  188.  
  189. {--------------------------------------------------------------}
  190. {         ISR - Interrupt Service Routine                      }
  191. {--------------------------------------------------------------}
  192.  
  193. PROCEDURE Async_ISR ; INTERRUPT ;
  194. { Interrupt Service Routine }
  195. { Invoked when the USART has received a byte of data from the  }
  196. { comm line re-written 9/10/84 in machine language ; original  }
  197. { source left as comments re-written 1987 to work under Turbo  }
  198. { Pascal Version 4.0                                           }
  199.  
  200. BEGIN { ISR }
  201.   inline($FB/                                { STI }
  202.  
  203.     { get the incoming character }
  204.     { Async_Buffer[Async_Buffer_Head] :=
  205.                     CHR( port[Async_Base + UART_RBR] ) ;       }
  206.     $8B/$16/Async_Base/                    { MOV DX,Base       }
  207.     $EC/                                   { IN AL,DX          }
  208.     $8B/$1E/Async_Buffer_Head/             { MOV BX,BufferHead }
  209.     $88/$87/Async_Buffer/                  { MOV Buffer[BX],AL }
  210.  
  211.     { Async_Buffer_NewHead := SUCC( Async_Buffer_Head ) ;      }
  212.     $43/                                   { INC BX            }
  213.  
  214.     { IF Async_Buffer_NewHead > Async_Buffer_Max
  215.                               THEN Async_Buffer_NewHead := 0 ; }
  216.     $81/$FB/Async_Buffer_Max/              { CMP BX,BufferMax  }
  217.     $7E/$02/                               { JLE L001          }
  218.     $33/$DB/                               { XOR BX,BX         }
  219.  
  220.     { IF Async_Buffer_NewHead = Async_Buffer_Tail THEN Overflow}
  221.     { := TRUE                                                  }
  222.     {L001:}
  223.     $3B/$1E/Async_Buffer_Tail/      { CMP BX,Async_Buffer_Tail }
  224.     $75/$08/                               { JNE L002          }
  225.     $C6/$06/Async_Buffer_Overflow/$01/     { MOV Overflow,1    }
  226.     $90/                                   { NOP generated by  }
  227.                                            { assembler         }
  228.     $EB/$16/                               { JMP SHORT L003    }
  229.     { ELSE BEGIN                                               }
  230.     { Async_Buffer_Head := Async_Buffer_NewHead ;              }
  231.     { Async_Buffer_Used  := SUCC( Async_Buffer_Used ) ;        }
  232.     { IF Async_Buffer_Used > Async_MaxBufferUsed THEN          }
  233.     {  Async_MaxBufferUsed := Async_BufferUsed                 }
  234.     {   END ;                                                  }
  235.     {L002:}
  236.     $89/$1E/Async_Buffer_Head/             { MOV BufferHead,BX }
  237.     $FF/$06/Async_Buffer_Used/          { INC Async_BufferUsed }
  238.     $8B/$1E/Async_Buffer_Used/       { MOV BX,Async_BufferUsed }
  239.     $3B/$1E/Async_MaxBufferUsed/  { CMP BX,Async_MaxBufferUsed }
  240.     $7E/$04/                               { JLE L003          }
  241.     $89/$1E/Async_MaxBufferUsed/  { MOV Async_MaxBufferUsed,BX }
  242.     {L003:}
  243.  
  244.     $FA/                                   { CLI               }
  245.  
  246.                                       { issue non-specific EOI }
  247.     { port[$20] := $20 ;                                       }
  248.     $B0/$20/                               { MOV AL,20h        }
  249.     $E6/$20                                { OUT 20h,AL        }
  250.     )
  251.   END { Async_ISR } ;
  252.  
  253.  
  254. procedure Async_Init;
  255. { initialize variables }
  256. begin
  257.   Async_Open_Flag := FALSE;
  258.   Async_Buffer_Overflow := FALSE;
  259.   Async_Buffer_Used := 0;
  260.   Async_MaxBufferUsed := 0;
  261. end; { Async_Init }
  262.  
  263. procedure Async_Close;
  264. { reset the interrupt system when UART interrupts              }
  265. { no longer needed                                             }
  266. var
  267.   i, m : Word;
  268. begin
  269.   if Async_Open_Flag then
  270.     begin
  271.  
  272.       { disable the IRQ on the 8259 }
  273.       Inline($FA);                         { disable interrupts }
  274.       i := Port[I8088_IMR];   { get the interrupt mask register }
  275.       m := 1 shl Async_Irq;    { set mask to turn off interrupt }
  276.       Port[I8088_IMR] := i or m;
  277.  
  278.       { disable the 8250 data ready interrupt }
  279.       Port[UART_IER + Async_Base] := 0;
  280.  
  281.       { disable OUT2 on the 8250 }
  282.       Port[UART_MCR + Async_Base] := 0;
  283.       Inline($FB);                         { enable interrupts  }
  284.  
  285.       { re-initialize our data areas so we know the port is     }
  286.       { closed                                                  }
  287.       Async_Open_Flag := FALSE;
  288.  
  289.       { Version 4 support by Keith Hawes next 2 lines           }
  290.       SetIntVec( Async_IRQ + 8, @@Async_Interrupt_Save );
  291.                                         { Restore old interrupt }
  292.       ExitProc := Async_ExitProc_Save; { Restore ExitProc chain}
  293.     end
  294. end; { Async_Close }
  295.  
  296. function Async_Open(ComPort       : Word;
  297.                     BaudRate      : Word;
  298.                     Parity        : Char;
  299.                     WordSize      : Word;
  300.                     StopBits      : Word) : Boolean;
  301. { open a communications port }
  302. var
  303.   ComParm : Word;
  304.   i, m : Word;
  305. begin
  306.   if Async_Open_Flag then Async_Close;
  307.  
  308.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  309.     Async_Port := 2
  310.   else
  311.     Async_Port := 1;  { default to COM1 }
  312.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  313.   Async_Irq := Hi(Async_Base) + 1;
  314.  
  315.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  316.     Async_Open := FALSE
  317.   else
  318.     begin
  319.       Async_Buffer_Head := 0;
  320.       Async_Buffer_Tail := 0;
  321.       Async_Buffer_Overflow := FALSE;
  322.  
  323.   { Build the ComParm for RS232_Init }
  324.   { See Technical Reference Manual for description }
  325.  
  326.       ComParm := $0000;
  327.  
  328.   { Set up the bits for the baud rate }
  329.       i := 0;
  330.       repeat
  331.         i := i + 1
  332.       until (Async_Baud_Table[i].Baud = BaudRate)
  333.               or (i = Async_Num_Bauds);
  334.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  335.  
  336.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  337.       else if Parity in ['O', 'o'] then
  338.            ComParm := ComParm or $0008
  339.       else ComParm := ComParm or $0000;  { default to No parity }
  340.       if WordSize = 7 then ComParm := ComParm or $0002
  341.       else ComParm := ComParm or $0003;
  342.                                        { default to 8 data bits }
  343.  
  344.       if StopBits = 2 then ComParm := ComParm or $0004
  345.       else ComParm := ComParm or $0000;
  346.                                         { default to 1 stop bit }
  347.  
  348.       { use the BIOS COM port initialization routine            }
  349.       { to save typing the code                                 }
  350.  
  351.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  352.       GetIntVec( Async_Irq + 8, Async_Interrupt_Save );
  353.                                          { Version 4 support KH }
  354.       Async_ExitProc_Save := ExitProc; { Version 4 support KH }
  355.       ExitProc := @@Async_Close;         { Version 4 support KH }
  356.       SetIntVec( Async_Irq + 8, @@Async_Isr );
  357.                                          { Version 4 support KH }
  358.  
  359. { Read the RBR and reset any possible pending error conditions. }
  360. { First turn off the Divisor Access Latch Bit to allow access to}
  361. { RBR, etc.                                                     }
  362.  
  363.       Inline($FA);                         { disable interrupts }
  364.  
  365.       Port[UART_LCR + Async_Base] :=
  366.               Port[UART_LCR + Async_Base] and $7F;
  367.       { read the Line Status Register to reset any errors it    }
  368.       { indicates                                               }
  369.       i := Port[UART_LSR + Async_Base];
  370.       { read the Receiver Buffer Register in case it contains a }
  371.       { character                                               }
  372.       i := Port[UART_RBR + Async_Base];
  373.  
  374.       { enable the irq on the 8259 controller                   }
  375.       i := Port[I8088_IMR];   { get the interrupt mask register }
  376.       m := (1 shl Async_Irq) xor $00FF;
  377.       Port[I8088_IMR] := i and m;
  378.  
  379.       { enable the data ready interrupt on the 8250             }
  380.       Port[UART_IER + Async_Base] := $01;
  381.       { enable data ready interrupt                             }
  382.  
  383.       { enable OUT2 on 8250                                     }
  384.       i := Port[UART_MCR + Async_Base];
  385.       Port[UART_MCR + Async_Base] := i or $08;
  386.       Inline($FB);                         { enable interrupts  }
  387.       Async_Open_Flag := TRUE;          { bug fix by Scott Herr }
  388.       Async_Open := TRUE
  389.     end;
  390. end; { Async_Open }
  391.  
  392. function Async_Buffer_Check(var C : Char) : Boolean;
  393.       { see if a character has been received; return it if yes  }
  394. begin
  395.   if Async_Buffer_Head = Async_Buffer_Tail then
  396.     Async_Buffer_Check := FALSE
  397.   else
  398.     begin
  399.       C := Async_Buffer[Async_Buffer_Tail];
  400.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  401.       if Async_Buffer_Tail > Async_Buffer_Max then
  402.         Async_Buffer_Tail := 0;
  403.       Async_Buffer_Used := Async_Buffer_Used - 1;
  404.       Async_Buffer_Check := TRUE
  405.     end
  406. end; { Async_Buffer_Check }
  407.  
  408. procedure Async_Send(C : Char);
  409.                                          { transmit a character }
  410. var
  411.   i, m, counter : Word;
  412. begin
  413.   Port[UART_MCR + Async_Base] := $0B;
  414.                                    { turn on OUT2, DTR, and RTS }
  415.  
  416.                                            { wait for CTS       }
  417.   counter := MaxInt;
  418.   while (counter <> 0) and
  419.         ((Port[UART_MSR + Async_Base] and $10) = 0) do
  420.     counter := counter - 1;
  421.  
  422.                  { wait for Transmit Hold Register Empty (THRE) }
  423.   if counter <> 0 then counter := MaxInt;
  424.   while (counter <> 0) and
  425.         ((Port[UART_LSR + Async_Base] and $20) = 0) do
  426.     counter := counter - 1;
  427.   if counter <> 0 then
  428.     begin
  429.                                            { send the character }
  430.       Inline($FA);                         { disable interrupts }
  431.       Port[UART_THR + Async_Base] := Ord(C);
  432.       Inline($FB)                          { enable interrupts  }
  433.     end
  434.   else
  435.     writeln('<<<TIMEOUT>>>');
  436. end; { Async_Send }
  437.  
  438. procedure Async_Send_String(S : String);
  439. { transmit a string }
  440. var
  441.   i : Word;
  442. begin
  443.   for i := 1 to length(S) do
  444.     Async_Send(S[i])
  445. end; { Async_Send_String }
  446.  
  447. procedure Async_Change(BaudRate      : Word;
  448.                        Parity        : Char;
  449.                        WordSize      : Word;
  450.                        StopBits      : Word);
  451. { change communication parameters "on the fly"                 }
  452. { you cannot use the BIOS routines because they drop DTR       }
  453.  
  454. const num_bauds = 15;
  455.     divisor_table : array [1..num_bauds] of record
  456.                                             baud, divisor : Word
  457.                                           end
  458.        = ((baud:50;  divisor:2304),
  459.           (baud:75;  divisor:1536),
  460.           (baud:110; divisor:1047),
  461.           (baud:134; divisor:857),
  462.           (baud:150; divisor:768),
  463.           (baud:300; divisor:384),
  464.           (baud:600; divisor:192),
  465.           (baud:1200; divisor:96),
  466.           (baud:1800; divisor:64),
  467.           (baud:2000; divisor:58),
  468.           (baud:2400; divisor:48),
  469.           (baud:3600; divisor:32),
  470.           (baud:4800; divisor:24),
  471.           (baud:7200; divisor:16),
  472.           (baud:9600; divisor:12));
  473.  
  474. var i : Word;
  475.     dv  : Word;
  476.     lcr : Word;
  477. begin
  478.  
  479.   { Build the Line Control Register and find                   }
  480.   { the divisor (for the baud rate)                            }
  481.  
  482.   { Set up the divisor for the baud rate                       }
  483.   i := 0;
  484.   repeat
  485.     i := i + 1
  486.   until (Divisor_Table[i].Baud = BaudRate) or (i = Num_Bauds);
  487.   dv  := Divisor_Table[i].divisor;
  488.  
  489.   lcr := 0;
  490.   case Parity of
  491.     'E' : lcr := lcr or $18;  { even parity }
  492.     'O' : lcr := lcr or $08;  { odd parity }
  493.     'N' : lcr := lcr or $00;  { no parity }
  494.     'M' : lcr := lcr or $28;  { Mark parity }
  495.     'S' : lcr := lcr or $38;  { Space parity }
  496.   else
  497.     lcr := lcr or $00;  { default to no parity }
  498.   end;
  499.  
  500.   case WordSize of
  501.     5 : lcr := lcr or $00;
  502.     6 : lcr := lcr or $01;
  503.     7 : lcr := lcr or $02;
  504.     8 : lcr := lcr or $03;
  505.   else
  506.     lcr := lcr or $03;  { default to 8 data bits }
  507.   end;
  508.  
  509.   if StopBits = 2 then lcr := lcr or $04
  510.   else lcr := lcr or $00;  { default to 1 stop bit }
  511.  
  512.   lcr := lcr and $7F;   { make certain the DLAB is off }
  513.  
  514.   Inline($FA);  { disable interrupts }
  515.  
  516.   { turn on DLAB to access the divisor                         }
  517.   Port[UART_LCR + Async_Base] := Port[UART_LCR +
  518.                                      Async_Base] or $80;
  519.  
  520.   { set the divisor                                            }
  521.   Port[Async_Base] := Lo(dv);
  522.   Port[Async_Base + 1] := Hi(dv);
  523.  
  524.   { turn off the DLAB and set the new comm. parameters         }
  525.   Port[UART_LCR + Async_Base] := lcr;
  526.  
  527.   Inline($FB);  { enable interrupts }
  528.  
  529. end; { Async_Change }
  530. end.
  531.  
  532. *****************************************************************
  533.   Test Program.... place in a separate file and compile with the
  534. Make option.
  535.  
  536. program tty;
  537. uses crt,async;
  538. var
  539.   c : char;
  540.  
  541. begin
  542.   Async_Init;  { initialize variables }
  543.   if not Async_Open(2, 1200, 'E', 7, 1) then
  544.                                      { open communications port }
  545.     begin
  546.       writeln('**ERROR: Async_Open failed');
  547.       halt
  548.     end;
  549.  
  550.   writeln('TTY Emulation begins now...');
  551.   writeln('Press ESC key to terminate...');
  552.  
  553.   repeat
  554.     if Async_Buffer_Check(c) then
  555.       case c of
  556.         #000 : ;  { strip incoming nulls }
  557.         #010 : ;  { strip incoming line feeds }
  558.         #012 : ClrScr;  { clear screen on a form feed }
  559.         #013 : Writeln  { handle carriage return as CR/LF }
  560.       else
  561.         write(c)  { else write incoming char to the screen }
  562.       end; { case }
  563.     if KeyPressed then
  564.       begin
  565.         c := readkey;
  566.         if c = #027 then  { Trap Esc Key }
  567.           begin
  568.             Async_Close;   { reset the interrupt system, etc. }
  569.             Writeln('End of TTY Emulation...');
  570.             halt;          { terminate the program }
  571.           end
  572.         else
  573.           Async_Send(c)
  574.       end;
  575.   until FALSE;
  576. end.
  577.  
  578. ****************************************************************
  579.