home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / pcmag / async4.zip / ASYNC4U.PAS < prev   
Pascal/Delphi Source File  |  1987-11-29  |  16KB  |  417 lines

  1. {----------------------------------------------------------------------}
  2. {                          ASYNC4U.PAS                                 }
  3. {                                                                      }
  4. {  This is a faithful translation of the famous ASYNC.INC by Michael   }
  5. {  Quinlan into a Turbo 4.0 unit.  No extra frills, no modification of }
  6. {  types, nothing fancy.  But with this code you should be able to     }
  7. {  delete your $I ASYNC.INC directive, add a USES ASYNC4U statement,   }
  8. {  and recompile your existing program.  If you want to add support    }
  9. {  for more ports, other computers, or change to use the new data      }
  10. {  types, all good ideas, go right ahead. With this you don't have to. }
  11. {                                                                      }
  12. {                                      Scott Gurvey, November 29 1987  }
  13. {----------------------------------------------------------------------}
  14. {                                                                      }
  15. {                          ASYNC.INC                                   }
  16. {                                                                      }
  17. {  Async Communication Routines                                        }
  18. {  by Michael Quinlan                                                  }
  19. {  with a bug fixed by Scott Herr                                      }
  20. {  made PCjr-compatible by W. M. Miller                                }
  21. {  Highly dependant on the IBM PC and PC DOS 2.0                       }
  22. {                                                                      }
  23. {  based on the DUMBTERM program by CJ Dunford in the January 1984     }
  24. {  issue of PC Tech Journal.                                           }
  25. {                                                                      }
  26. {  Entry points:                                                       }
  27. {                                                                      }
  28. {    Async_Init                                                        }
  29. {      Performs initialization.                                        }
  30. {                                                                      }
  31. {    Async_Open(Port, Baud : Integer;                                  }
  32. {               Parity : Char;                                         }
  33. {               WordSize, StpBits : Integer) : Boolean                 }
  34. {      Sets up interrupt vector, initialies the COM port for           }
  35. {      processing, sets pointers to the buffer.  Returns FALSE if COM  }
  36. {      port not installed.                                             }
  37. {                                                                      }
  38. {    Async_Buffer_Check(var C : Char) : Boolean                        }
  39. {      If a character is available, returns TRUE and moves the         }
  40. {        character from the buffer to the parameter                    }
  41. {      Otherwise, returns FALSE                                        }
  42. {                                                                      }
  43. {    Async_Send(C : Char)                                              }
  44. {      Transmits the character.                                        }
  45. {                                                                      }
  46. {    Async_Send_String(S : LStr)                                       }
  47. {      Calls Async_Send to send each character of S.                   }
  48. {                                                                      }
  49. {    Async_Close                                                       }
  50. {      Turn off the COM port interrupts.                               }
  51. {      **MUST** BE CALLED BEFORE EXITING YOUR PROGRAM; otherwise you   }
  52. {      will see some really strange errors and have to re-boot.        }
  53. {                                                                      }
  54. {----------------------------------------------------------------------}
  55.  
  56. {$B-} { Short circuit boolean ON }
  57. {$I-} { I/O hecking OFF }
  58. {$R-} { Range checking OFF }
  59. {$S-} { Stack checking OFF }
  60. {$V-} { Var-str checking OFF}
  61.  
  62. UNIT ASYNC4U;
  63.  
  64. INTERFACE
  65.  
  66. USES Dos;
  67.  
  68. { global declarations }
  69.  
  70. type
  71.   LStr = String[255];  { generic string type for parameters }
  72.  
  73. const
  74.   Async_Buffer_Max = 4095;
  75.  
  76. var
  77.   Async_OriginalVector : pointer;
  78.   Async_Buffer       : Array[0..Async_Buffer_Max] of char;
  79.  
  80.   Async_Open_Flag    : Boolean;   { true if Open but no Close }
  81.   Async_Port         : Integer;   { current Open port number (1 or 2) }
  82.   Async_Base         : Integer;   { base for current open port }
  83.   Async_Irq          : Integer;   { irq for current open port }
  84.  
  85.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  86.   Async_Buffer_Used     : Integer;
  87.   Async_MaxBufferUsed   : Integer;
  88.  
  89.     { Async_Buffer is empty if Head = Tail }
  90.   Async_Buffer_Head  : Integer;   { Locn in Async_Buffer to put next char }
  91.   Async_Buffer_Tail  : Integer;   { Locn in Async_Buffer to get next char }
  92.   Async_Buffer_NewTail : Integer;
  93.  
  94. {----------------------------------------------------------------------------}
  95. {                          USER CALLABLE ROUTINES                            }
  96. {----------------------------------------------------------------------------}
  97.  
  98. procedure Async_Init;
  99. { initialize variables }
  100.  
  101. procedure Async_Close;
  102. { reset the interrupt system when UART interrupts no longer needed }
  103.  
  104. function Async_Open(ComPort       : Integer;
  105.                     BaudRate      : Integer;
  106.                     Parity        : Char;
  107.                     WordSize      : Integer;
  108.                     StopBits      : Integer) : Boolean;
  109. { open a communications port }
  110.  
  111. function Async_Buffer_Check(var C : Char) : Boolean;
  112. { see if a character has been received; return it if yes }
  113.  
  114. procedure Async_Send(C : Char);
  115. { transmit a character }
  116.  
  117. procedure Async_Send_String(S : LStr);
  118. { transmit a string }
  119.  
  120. {----------------------------------------------------------------------------}
  121.  
  122. IMPLEMENTATION
  123.  
  124.  
  125. const
  126.   UART_THR = $00;    { offset from base of UART Registers for IBM PC }
  127.   UART_RBR = $00;
  128.   UART_IER = $01;
  129.   UART_IIR = $02;
  130.   UART_LCR = $03;
  131.   UART_MCR = $04;
  132.   UART_LSR = $05;
  133.   UART_MSR = $06;
  134.  
  135.   I8088_IMR = $21;   { port address of the Interrupt Mask Register }
  136.  
  137.  
  138. var
  139.  
  140.   Async_BIOS_Port_Table : Array[1..2] of Integer absolute $40:0;
  141.                { This table is initialized by BIOS equipment determination
  142.                  code at boot time to contain the base addresses for the
  143.                  installed async adapters.  A value of 0 means "not in-
  144.                  stalled." }
  145.  
  146. const
  147.   Async_Num_Bauds = 8;
  148.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  149.                                                      Baud, Bits : integer
  150.                                                    end
  151.                    = ((Baud:110;  Bits:$00),
  152.                       (Baud:150;  Bits:$20),
  153.                       (Baud:300;  Bits:$40),
  154.                       (Baud:600;  Bits:$60),
  155.                       (Baud:1200; Bits:$80),
  156.                       (Baud:2400; Bits:$A0),
  157.                       (Baud:4800; Bits:$C0),
  158.                       (Baud:9600; Bits:$E0));
  159.  
  160.  
  161. PROCEDURE DisableInterrupts; inline($FA {cli} );     {MACROS}
  162. PROCEDURE EnableInterrupts;  inline($FB {sti} );
  163.  
  164.  
  165. procedure BIOS_RS232_Init(ComPort, ComParm : Integer);
  166. { Issue Interrupt $14 to initialize the UART }
  167. { See the IBM PC Technical Reference Manual for the format of ComParm }
  168. var
  169.   Regs : registers;
  170. begin
  171.   with Regs do
  172.     begin
  173.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  174.       dx := ComPort;
  175.       Intr($14, Regs)
  176.     end
  177. end; { BIOS_RS232_Init }
  178.  
  179.  
  180. {----------------------------------------------------------------------}
  181. {                                                                      }
  182. {  ISR - Interrupt Service Routine                                     }
  183. {                                                                      }
  184. {----------------------------------------------------------------------}
  185.  
  186. procedure Async_Isr;  INTERRUPT;
  187. { Interrupt Service Routine }
  188. { Invoked when the UART has received a byte of data from the
  189.   communication line }
  190.  
  191. { re-written 9/10/84 to be entirely in machine language; original source
  192.   left as comments }
  193.  
  194. begin
  195.  
  196.   Inline(
  197.     $FB/                           { STI }
  198.       { get the incomming character }
  199.       { Async_Buffer[Async_Buffer_Head] := Chr(Port[UART_RBR + Async_Base]); }
  200.     $8B/$16/Async_Base/            { MOV DX,Async_Base }
  201.     $EC/                           { IN AL,DX }
  202.     $8B/$1E/Async_Buffer_Head/     { MOV BX,Async_Buffer_Head }
  203.     $88/$87/Async_Buffer/          { MOV Async_Buffer[BX],AL }
  204.       { Async_Buffer_NewHead := Async_Buffer_Head + 1; }
  205.     $43/                           { INC BX }
  206.       { if Async_Buffer_NewHead > Async_Buffer_Max then
  207.           Async_Buffer_NewHead := 0; }
  208.     $81/$FB/Async_Buffer_Max/      { CMP BX,Async_Buffer_Max }
  209.     $7E/$02/                       { JLE L001 }
  210.     $33/$DB/                       { XOR BX,BX }
  211.       { if Async_Buffer_NewHead = Async_Buffer_Tail then
  212.           Async_Buffer_Overflow := TRUE
  213.         else }
  214. {L001:}
  215.     $3B/$1E/Async_Buffer_Tail/     { CMP BX,Async_Buffer_Tail }
  216.     $75/$08/                       { JNE L002 }
  217.     $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
  218.     $90/                           { NOP generated by assembler for some reason }
  219.     $EB/$16/                       { JMP SHORT L003 }
  220.       { begin
  221.           Async_Buffer_Head := Async_Buffer_NewHead;
  222.           Async_Buffer_Used := Async_Buffer_Used + 1;
  223.           if Async_Buffer_Used > Async_MaxBufferUsed then
  224.             Async_MaxBufferUsed := Async_Buffer_Used
  225.         end; }
  226. {L002:}
  227.     $89/$1E/Async_Buffer_Head/     { MOV Async_Buffer_Head,BX }
  228.     $FF/$06/Async_Buffer_Used/     { INC Async_Buffer_Used }
  229.     $8B/$1E/Async_Buffer_Used/     { MOV BX,Async_Buffer_Used }
  230.     $3B/$1E/Async_MaxBufferUsed/   { CMP BX,Async_MaxBufferUsed }
  231.     $7E/$04/                       { JLE L003 }
  232.     $89/$1E/Async_MaxBufferUsed/   { MOV Async_MaxBufferUsed,BX }
  233. {L003:}
  234.       { disable interrupts }
  235.     $FA/                           { CLI }
  236.       { Port[$20] := $20; }  { use non-specific EOI }
  237.     $B0/$20/                       { MOV AL,20h }
  238.     $E6/$20                        { OUT 20h,AL }
  239.        )
  240. end; { Async_Isr }
  241.  
  242. procedure Async_Init;
  243. { initialize variables }
  244. begin
  245.   Async_Open_Flag := FALSE;
  246.   Async_Buffer_Overflow := FALSE;
  247.   Async_Buffer_Used := 0;
  248.   Async_MaxBufferUsed := 0;
  249. end; { Async_Init }
  250.  
  251. procedure Async_Close;
  252. { reset the interrupt system when UART interrupts no longer needed }
  253. var
  254.   i, m : Integer;
  255. begin
  256.   if Async_Open_Flag then
  257.     begin
  258.  
  259.       { disable the IRQ on the 8259 }
  260.       DisableInterrupts;
  261.       i := Port[I8088_IMR];        { get the interrupt mask register }
  262.       m := 1 shl Async_Irq;        { set mask to turn off interrupt }
  263.       Port[I8088_IMR] := i or m;
  264.  
  265.       { disable the 8250 data ready interrupt }
  266.       Port[UART_IER + Async_Base] := 0;
  267.  
  268.       { disable OUT2 on the 8250 }
  269.       Port[UART_MCR + Async_Base] := 0;
  270.       EnableInterrupts;
  271.  
  272.       SetIntVec(Async_Irq + 8,Async_OriginalVector);
  273.  
  274.       { re-initialize our data areas so we know the port is closed }
  275.       Async_Open_Flag := FALSE
  276.  
  277.     end
  278. end; { Async_Close }
  279.  
  280. function Async_Open(ComPort       : Integer;
  281.                     BaudRate      : Integer;
  282.                     Parity        : Char;
  283.                     WordSize      : Integer;
  284.                     StopBits      : Integer) : Boolean;
  285. { open a communications port }
  286. var
  287.   ComParm : Integer;
  288.   i, m : Integer;
  289. begin
  290.   if Async_Open_Flag then Async_Close;
  291.  
  292.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  293.     Async_Port := 2
  294.   else
  295.     Async_Port := 1;  { default to COM1 }
  296.   Async_Base := Async_BIOS_Port_Table[Async_Port];
  297.   Async_Irq := Hi(Async_Base) + 1;
  298.  
  299.   if (Port[UART_IIR + Async_Base] and $00F8) <> 0 then
  300.     Async_Open := FALSE
  301.   else
  302.     begin
  303.       Async_Buffer_Head := 0;
  304.       Async_Buffer_Tail := 0;
  305.       Async_Buffer_Overflow := FALSE;
  306.  
  307.   { Build the ComParm for RS232_Init }
  308.   { See Technical Reference Manual for description }
  309.  
  310.       ComParm := $0000;
  311.  
  312.   { Set up the bits for the baud rate }
  313.       i := 0;
  314.       repeat
  315.         i := i + 1
  316.       until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds);
  317.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  318.  
  319.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  320.       else if Parity in ['O', 'o'] then ComParm := ComParm or $0008
  321.       else ComParm := ComParm or $0000;  { default to No parity }
  322.  
  323.       if WordSize = 7 then ComParm := ComParm or $0002
  324.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  325.  
  326.       if StopBits = 2 then ComParm := ComParm or $0004
  327.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  328.  
  329.   { use the BIOS COM port initialization routine to save typing the code }
  330.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  331.  
  332.       GetIntVec(Async_Irq + 8, Async_OriginalVector);
  333.       SetIntVec(Async_Irq + 8, @Async_Isr);
  334.  
  335.   { read the RBR and reset any possible pending error conditions }
  336.   { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }
  337.  
  338.       DisableInterrupts;
  339.  
  340.       Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] and $7F;
  341.   { read the Line Status Register to reset any errors it indicates }
  342.       i := Port[UART_LSR + Async_Base];
  343.   { read the Receiver Buffer Register in case it contains a character }
  344.       i := Port[UART_RBR + Async_Base];
  345.  
  346.   { enable the irq on the 8259 controller }
  347.       i := Port[I8088_IMR];  { get the interrupt mask register }
  348.       m := (1 shl Async_Irq) xor $00FF;
  349.       Port[I8088_IMR] := i and m;
  350.  
  351.   { enable the data ready interrupt on the 8250 }
  352.       Port[UART_IER + Async_Base] := $01; { enable data ready interrupt }
  353.  
  354.   { enable OUT2 on 8250 }
  355.       i := Port[UART_MCR + Async_Base];
  356.       Port[UART_MCR + Async_Base] := i or $08;
  357.  
  358.       EnableInterrupts;
  359.       Async_Open_Flag := TRUE;  { bug fix by Scott Herr }
  360.       Async_Open := TRUE
  361.     end
  362. end; { Async_Open }
  363.  
  364. function Async_Buffer_Check(var C : Char) : Boolean;
  365. { see if a character has been received; return it if yes }
  366. begin
  367.   if Async_Buffer_Head = Async_Buffer_Tail then
  368.     Async_Buffer_Check := FALSE
  369.   else
  370.     begin
  371.       C := Async_Buffer[Async_Buffer_Tail];
  372.       Async_Buffer_Tail := Async_Buffer_Tail + 1;
  373.       if Async_Buffer_Tail > Async_Buffer_Max then
  374.         Async_Buffer_Tail := 0;
  375.       Async_Buffer_Used := Async_Buffer_Used - 1;
  376.       Async_Buffer_Check := TRUE
  377.     end
  378. end; { Async_Buffer_Check }
  379.  
  380. procedure Async_Send(C : Char);
  381. { transmit a character }
  382. var
  383.   i, m, counter : Integer;
  384. begin
  385.   Port[UART_MCR + Async_Base] := $0B; { turn on OUT2, DTR, and RTS }
  386.  
  387.   { wait for CTS }
  388.   counter := MaxInt;
  389.   while (counter <> 0) and ((Port[UART_MSR + Async_Base] and $10) = 0) do
  390.     counter := counter - 1;
  391.  
  392.   { wait for Transmit Hold Register Empty (THRE) }
  393.   if counter <> 0 then counter := MaxInt;
  394.   while (counter <> 0) and ((Port[UART_LSR + Async_Base] and $20) = 0) do
  395.     counter := counter - 1;
  396.  
  397.   if counter <> 0 then
  398.     begin
  399.       { send the character }
  400.       DisableInterrupts;
  401.       Port[UART_THR + Async_Base] := Ord(C);
  402.       EnableInterrupts
  403.     end
  404.   else
  405.     writeln('<<<TIMEOUT>>>');
  406.  
  407. end; { Async_Send }
  408.  
  409. procedure Async_Send_String(S : LStr);
  410. { transmit a string }
  411. var
  412.   i : Integer;
  413. begin
  414.   for i := 1 to length(S) do
  415.     Async_Send(S[i])
  416. end; { Async_Send_String }
  417. end. { ASYNC4U UNIT }