[comp.lang.pascal] Turbo Pascal serial port I/O

apuzzo@horton.Colorado.EDU (APUZZO ALFONSO A) (05/11/91)

Help!

I need to do serial port I/O under TP6 without using inline assembly or 
3rd party libraries (unless they're shareware/PD)  I need to be able to do the
kinds of things a term program does: send/recieve data, set baud rate, parity
etc... this is for interfacing with a custom hardware device (not a modem)
I don't need any snazzy error detecting/correcting protocols (like X/Y/Zmodem,
Kermit etc)  I just need to be able to pump bits in and out of COM1 or COM2
on a MSDOS machine.

Please mail response and I'll post a summary if there is additional interest

-Tony
apuzzo@yertle.Colorado.EDU

-- 
*                                                         
* Be a non-conformist like me and don't use a .sig at all.
*                                                         

rind@popvax.uucp (747707@d.rind) (05/14/91)

program Modemo;
{This is a demo of how to use the comports (COM1 and COM2 only) from
Turbo Pascal.  It is just stripped down code, to give you an idea
of how to deal with the ports.  If the code looks odd, it's because
I've taken it from a real application of mine and just left the
parts necessary to initialize the ports and send and receive
characters.  Typing a capital 'S' should stop the program.

David Rind
rind@popvax.harvard.edu
}

Uses  dos, crt;

const
  baud: array[2..7] of word=(300,600,1200,2400,4800,9600);  {Baud rates}
  Parity : Byte = 3; {Even parity}
  Stopbits : Byte = 0; {1 stop bits}
  Wordlength : Byte = 2; {7 bit words}
  PortBufSize = 10000;

var
  ch: char;   {Current character}
  regs: registers;
  base: word;
  com1,com2: pointer;   {Address of com-port interrupt vector saved here}
  hd,tl : word;   {Head and tail of com-port buffer}
  bd,comport,holdint: byte;
  buf: array[0..portbufsize] of byte;
  sb,quit : Boolean;
  x : integer;

procedure send(ch: char);    {Send character out COM port}
  begin
    while port[base+5] and $20=0 do;  {Wait for ready to receive message from port}
    port[base]:=ord(ch);   {Send character}
  end;

procedure alt(st: string);  
  var i:integer;
  begin
    for i:=1 to length(st) do send(st[i]);
    ch:=#0;
  end;

Function GetBufChar(Var Ch : Char) : Boolean;
{Get a character from the buffer and return it by reference in Ch.
Function returns False if buffer was empty, True otherwise.}
  Begin
    If hd = tl then GetBufChar := False
    Else {Something in buffer}
    Begin
      GetBufChar := True;
      Ch := Chr(buf[tl]);
      If sb and ((hd+portbufsize-tl) mod portbufsize < 65) then
      Begin  {^S pending and buffer now with < 65 characters in it.}
        sb := False;
      End;
      Inc(tl);
      If tl = portbufsize then tl := 0;
    End;
  End;


procedure input(flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP: word);
  interrupt;
  begin
    if port[base+5] and 1=1 then      {If bit 0=1 then something came in the com port}
      Begin
        buf[hd]:=port[base];                 {Put new character from com1 into buffer}
        inc(hd);
        if hd = portbufsize then hd := 0;
        if not sb and ((hd+portbufsize-tl) mod portbufsize > (portbufsize-100) ) then begin  {Running out of buffer room?}
          send(^S);         {Control S}
          sb:=true;
        end;
      End;
      port[$20]:=$20;    {Clear the interrupt}
  end;


begin
  bd:=4;        {set baud rate=1200}
  Comport := 1;
  regs.AX:=(bd*32)+(Parity*8)+(Stopbits*4)+Wordlength;
   {Set up for setting baud rate of serial port}
  regs.DX:=comport-1; {for com1, DX := 0}
  intr($14,regs);    {Set baud rate, etc. for port}
  getintvec($0C,com1);    {Save old interrupt for com1}
  getintvec($0B,com2);    {Save old interrupt for com2}
  hd:=0; tl:=0; {Head and Tail of buffer}
  Holdint := port[$21]; {Save initial status of interrupt enables.}
  if comport = 1 then
  Begin
    setintvec($0C,@input); {Redirect input from Com1 to procedure Input}
    port[$21]:=port[$21] and $EF; {Enable interrupts for Com1}
    base:=memw[$40:$00]; {Get base address of Com1}
  End
  Else
  Begin
    Setintvec($0B,@input); {Redirect input from Com2 to procedure Input}
    port[$21]:=port[$21] and $F7; {Enable interrupts for Com2}
    base := memw[$40:$02]; {Get base address of Com2}
  End;
  {The next 3 lines have to do with setting up the modem control
   register.  I'd suggest getting a book if you really need to
   understand this.}
  port[base+3]:=port[base+3] and $7F;
  port[base+1]:=$01;
  port[base+4]:=$09;
  x:=port[base]; port[$20]:=$20;  {Read from port to initialize
                                   and clear port}
  quit := false;
  repeat
    if keypressed then
    begin   {Accept input from keyboard!}
      ch := readkey;
      if ch='S' then quit := True;
      send(ch);  {Send ASCII value of character out com1}
    end
    else if GetBufChar(ch) then
    begin
      write(ch);
    end;
  until quit;
  port[$21]:= Holdint;
  port[base+3]:=port[base+3] and $7F;
  port[base+1]:=$0;
  port[base+4]:=$0;
  setintvec($0C,com1);
  setintvec($0B,com2);
end.
David Rind
rind@popvax.harvard.edu