[Back to COMM SWAG index]  [Back to Main SWAG index]  [Original]


{$I-} { I/O hecking OFF }
{$R-} { Range checking OFF }
{$S-} { Stack checking OFF }
{$V-} { Var-str checking OFF}
{$B+} {Boolean complete evaluation on}
{$N-} {No numeric coprocessor}

Unit RS232INT;

Interface

Uses
  Crt,
  Dos;

{ global declarations  for Async}

type
  astr = String[160];  { generic string type for parameters      }

const
  buffer_max = 5120;

var
  Async_OriginalVector : pointer;
  buffer       : Array[0..buffer_max] of char;

  Async_Open_Flag    : Boolean;   { true if Open but no Close }
  Async_Port         : Integer;   { current Open port number (1 or 2) }
  base               : Integer;   { base for current open port }
  Async_Irq          : Integer;   { irq for current open port }

  Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  Async_Buffer_Used     : Integer;
  Async_MaxBufferUsed   : Integer;

                 { buffer is empty if Head = Tail }
  Buffer_head  : Integer;   { Locn in buffer to put next char }
  Buffer_tail  : Integer;   { Locn in buffer to get next char }
  Buffer_newtail : Integer;


{ End of Async declarations }

procedure async_isr; INTERRUPT;
procedure set_baud(r:integer);
function charin:char;
procedure charout(c:char);
function cdet:boolean;
procedure Async_Init;
procedure Async_Close;
Procedure Async_Open(ComPort       : Integer;
                    BaudRate      : Integer;
                    Parity        : Char;
                    WordSize      : Integer;
                    StopBits      : Integer);

Implementation

const
  UART_THR = $00;
  UART_RBR = $00;
  UART_IER = $01;
  UART_IIR = $02;
  UART_LCR = $03;
  UART_MCR = $04;
  UART_LSR = $05;
  UART_MSR = $06;

  I8088_IMR = $21;   { port address of the Interrupt Mask Register }


var

  Async_BIOS_Port_Table : Array[1..4] of Integer absolute $40:0;

const
  Async_Num_Bauds = 8;
  Async_Baud_Table : array [1..Async_Num_Bauds] of record
                                                     Baud, Bits : integer
                                                   end
                   = ((Baud:110;  Bits:$00),
                      (Baud:150;  Bits:$20),
                      (Baud:300;  Bits:$40),
                      (Baud:600;  Bits:$60),
                      (Baud:1200; Bits:$80),
                      (Baud:2400; Bits:$A0),
                      (Baud:4800; Bits:$C0),
                      (Baud:9600; Bits:$E0));


PROCEDURE DisableInterrupts; inline($FA {cli} );     {MACROS}
PROCEDURE EnableInterrupts;  inline($FB {sti} );

procedure BIOS_RS232_Init(ComPort, ComParm : Integer);
var
  Regs : registers;
begin
  with Regs do
    begin
      ax := ComParm and $00FF;  { AH=0; AL=ComParm }
      dx := ComPort;
      Intr($14, Regs)
    end
end;

procedure Async_Isr;  {INTERRUPT;}
begin
  Inline(
    $FB/                           { STI }
      { get the incomming character }
      { buffer[Buffer_head] := Chr(Port[UART_RBR + base]); }
    $8B/$16/base/                  { MOV DX,base }
    $EC/                           { IN AL,DX }
    $8B/$1E/Buffer_head/           { MOV BX,Buffer_head }
    $88/$87/buffer/                { MOV buffer[BX],AL }
      { Async_Buffer_NewHead := Buffer_head + 1; }
    $43/                           { INC BX }
      { if Async_Buffer_NewHead > buffer_max then
          Async_Buffer_NewHead := 0; }
    $81/$FB/buffer_max/            { CMP BX,buffer_max }
    $7E/$02/                       { JLE L001 }
    $33/$DB/                       { XOR BX,BX }
      { if Async_Buffer_NewHead = Buffer_tail then
          Async_Buffer_Overflow := TRUE
        else }
{L001:}
    $3B/$1E/Buffer_tail/     { CMP BX,Buffer_tail }
    $75/$08/                       { JNE L002 }
    $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
    $90/                           { NOP generated by assembler for some reason }
    $EB/$16/                       { JMP SHORT L003 }
      { begin
          Buffer_head := Async_Buffer_NewHead;
          Async_Buffer_Used := Async_Buffer_Used + 1;
          if Async_Buffer_Used > Async_MaxBufferUsed then
            Async_MaxBufferUsed := Async_Buffer_Used
        end; }
{L002:}
    $89/$1E/Buffer_head/           { MOV Buffer_head,BX }
    $FF/$06/Async_Buffer_Used/     { INC Async_Buffer_Used }
    $8B/$1E/Async_Buffer_Used/     { MOV BX,Async_Buffer_Used }
    $3B/$1E/Async_MaxBufferUsed/   { CMP BX,Async_MaxBufferUsed }
    $7E/$04/                       { JLE L003 }
    $89/$1E/Async_MaxBufferUsed/   { MOV Async_MaxBufferUsed,BX }
{L003:}
      { disable interrupts }
    $FA/                           { CLI }
      { Port[$20] := $20; }        { use non-specific EOI }
    $B0/$20/                       { MOV AL,20h }
    $E6/$20                        { OUT 20h,AL }
       )
end; { Async_Isr }

procedure Async_Init;
begin
  Async_Open_Flag := FALSE;
  Async_Buffer_Overflow := FALSE;
  Async_Buffer_Used := 0;
  Async_MaxBufferUsed := 0;
end; { Async_Init }

procedure Async_Close;
var
  i, m : Integer;
begin
  if Async_Open_Flag then
    begin

      { disable the IRQ on the 8259 }
      DisableInterrupts;
      i := Port[I8088_IMR];        { get the interrupt mask register }
      m := 1 shl Async_Irq;        { set mask to turn off interrupt }
      Port[I8088_IMR] := i or m;

      { disable the 8250 data ready interrupt }
      Port[UART_IER + base] := 0;

      { disable OUT2 on the 8250 }
      Port[UART_MCR + base] := 0;
      EnableInterrupts;

      SetIntVec(Async_Irq + 8,Async_OriginalVector);

      { re-initialize our data areas so we know the port is closed }
      Async_Open_Flag := FALSE

    end
end; { Async_Close }

Procedure Async_Open(ComPort       : Integer;
                    BaudRate      : Integer;
                    Parity        : Char;
                    WordSize      : Integer;
                    StopBits      : Integer);
{ open a communications port }
var
  ComParm : Integer;
  i, m : Integer;
begin
  if Async_Open_Flag then Async_Close;

  if (ComPort = 4) and (Async_BIOS_Port_Table[4] <> 0) then
	Async_Port := 4;
  if (ComPort = 3) and (Async_BIOS_Port_Table[3] <> 0) then
	Async_Port := 3; 
  if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
    Async_Port := 2;
  if (ComPort = 1) and (Async_BIOS_Port_Table[1] <> 0) then
    Async_Port := 1;  { default to COM1 }
  base := Async_BIOS_Port_Table[Async_Port];
  Async_Irq := Hi(base) + 1;

  if (Port[UART_IIR + base] and $00F8)=0
  then
    begin
      Buffer_head := 0;
      Buffer_tail := 0;
      Async_Buffer_Overflow := FALSE;

  { Build the ComParm for RS232_Init }
  { See Technical Reference Manual for description }

      ComParm := $0000;

  { Set up the bits for the baud rate }
      i := 0;
      repeat
        i := i + 1
      until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds);
      ComParm := ComParm or Async_Baud_Table[i].Bits;

      if Parity in ['E', 'e'] then ComParm := ComParm or $0018
      else if Parity in ['O', 'o'] then ComParm := ComParm or $0008
      else ComParm := ComParm or $0000;  { default to No parity }

      if WordSize = 7 then ComParm := ComParm or $0002
      else ComParm := ComParm or $0003;  { default to 8 data bits }

      if StopBits = 2 then ComParm := ComParm or $0004
      else ComParm := ComParm or $0000;  { default to 1 stop bit }

  { use the BIOS COM port initialization routine to save typing the code }
      BIOS_RS232_Init(Async_Port - 1, ComParm);

      GetIntVec(Async_Irq + 8, Async_OriginalVector);
      SetIntVec(Async_Irq + 8, @Async_Isr);

  { read the RBR and reset any possible pending error conditions }
  { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }

      DisableInterrupts;

      Port[UART_LCR + base] := Port[UART_LCR + base] and $7F;
  { read the Line Status Register to reset any errors it indicates }
      i := Port[UART_LSR + base];
  { read the Receiver Buffer Register in case it contains a character }
      i := Port[UART_RBR + base];

  { enable the irq on the 8259 controller }
      i := Port[I8088_IMR];  { get the interrupt mask register }
      m := (1 shl Async_Irq) xor $00FF;
      Port[I8088_IMR] := i and m;

  { enable the data ready interrupt on the 8250 }
      Port[UART_IER + base] := $01; { enable data ready interrupt }

  { enable OUT2 on 8250 }
      i := Port[UART_MCR + base];
      Port[UART_MCR + base] := i or $08;

      EnableInterrupts;
      Async_Open_Flag := TRUE;
      {Async_Open := TRUE}
    end
end; { Async_Open }

procedure set_baud(r:integer);
var rl:real; a:byte;
begin
  if (r>=300) and (r<=9600) then begin
    rl:=115200.0/r;
    r:=trunc(rl);
    a:=port[3+base] or 128;
    port[base+3]:=a;
    port[base]:=lo(r);
    port[1+base]:=hi(r);
    port[3+base]:=a and 127;
  end;
end;

function charin:char;
var t:char;
begin
  if buffer_Head = buffer_Tail Then
    t:=#0
  else begin
    disableinterrupts;
    t:=buffer[buffer_Tail];
    buffer_Tail:=(buffer_Tail+1) mod (buffer_max+1);
    enableinterrupts;
  end;
 charin := t;
end;

procedure charout(c:char);
begin
  while (port[base+5] and 32)=0 do;
  port[base]:=ord(c);
end;

function cdet:boolean;
begin
  cdet:=(port[base+6] and 128)<>0;
end;
end.

[Back to COMM SWAG index]  [Back to Main SWAG index]  [Original]