home *** CD-ROM | disk | FTP | other *** search
/ norge.freeshell.org (192.94.73.8) / 192.94.73.8.tar / 192.94.73.8 / pub / computers / pcjr / comm / ASYNC.LZH / ASYNC.INC
Text File  |  1986-01-02  |  16KB  |  393 lines

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