[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]