telegard/pibo.pas

1145 lines
56 KiB
ObjectPascal

{$A+,B+,E+,F+,I+,L+,N-,O+,R-,S+,V-}
unit pibo;
interface
uses
crt,dos,
{rcg11172000 no overlay under Linux.}
{overlay,}
pibtimer,pibasync;
procedure async_receive_with_timeout(secs:integer; var c:integer);
procedure async_stuff(ch:char);
procedure async_find_delay(var one_ms_delay:integer);
procedure async_init(async_buffer_max,async_obuffer_max,
async_high_lev1,async_high_lev2,
async_low_lev:integer);
function async_carrier_drop:boolean;
function async_line_error(var error_flags:byte):boolean;
function async_ring_detect:boolean;
procedure async_send_break;
procedure async_send_string(s:anystr);
procedure async_send_string_with_delays(s:anystr; char_delay,eos_delay:integer);
function async_percentage_used:real;
function async_peek(nchars:integer):char;
procedure async_setup_port(comport,base_address,irq_line:integer);
procedure async_drain_output_buffer(max_wait_time:integer);
function async_port_address_given(com_port:integer):boolean;
procedure async_flush_output_buffer;
procedure async_close(drop_dtr:boolean);
procedure async_clear_errors;
procedure async_reset_port(comport:integer; baudrate:word; parity:char;
wordsize,stopbits:integer );
procedure async_release_buffers;
implementation
{uses pibasync;}
(*----------------------------------------------------------------------*)
(* Async_Receive_With_TimeOut --- Return char. from buffer with delay *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Receive_With_Timeout( Secs : INTEGER; VAR C : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Receive_With_Timeout *)
(* *)
(* Purpose: Retrieve character as integer from buffer, *)
(* or return TimeOut if specified delay period *)
(* expires. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Receive_With_Timeout( Secs: INTEGER; VAR C: INTEGER ); *)
(* *)
(* Secs --- Timeout period in seconds *)
(* NOTE: Cannot be longer than 32 seconds! *)
(* C --- ORD(character) (if any) retrieved from buffer; *)
(* set to TimeOut if no character found before *)
(* delay period expires. *)
(* *)
(* Calls: Async_Receive *)
(* *)
(* WATCH OUT! THIS ROUTINE RETURNS AN INTEGER, NOT A CHARACTER!!! *)
(* *)
(*----------------------------------------------------------------------*)
BEGIN (* Async_Receive_With_Timeout *)
INLINE(
{;}
{; Check if a character in input comm buffer}
{;}
$A1/>ASYNC_BUFFER_TAIL/ { MOV AX,[>Async_Buffer_Tail]}
$3B/$06/>ASYNC_BUFFER_HEAD/ { CMP AX,[>Async_Buffer_Head]}
$75/$29/ { JNE Rec1}
{;}
{; Buffer empty -- begin wait loop.}
{;}
$8B/$46/<SECS/ { MOV AX,[BP+<Secs] ;Get seconds to wait}
$B9/$0A/$00/ { MOV CX,10 ;Shift count = 2 ** 10 = 1024}
$D3/$E0/ { SHL AX,CL ;Seconds * 1024 = milleseconds}
$89/$C1/ { MOV CX,AX ;Move to looping register}
{;}
{; Delay for 1 ms.}
{;}
$51/ {Delay: PUSH CX ;Save milleseconds to go}
$8B/$0E/>ASYNC_ONEMSDELAY/ { MOV CX,[>Async_OneMSDelay] ;Get delay loop value for 1 ms}
$E2/$FE/ {Delay1: LOOP Delay1 ;Tight loop for 1 ms delay}
{;}
{; Check if any character yet.}
{;}
$59/ { POP CX ;Get back millesecond count}
{;}
$A1/>ASYNC_BUFFER_TAIL/ { MOV AX,[>Async_Buffer_Tail]}
$3B/$06/>ASYNC_BUFFER_HEAD/ { CMP AX,[>Async_Buffer_Head]}
$75/$0E/ { JNE Rec1}
{;}
{; Buffer still empty -- decrement elapsed time}
{;}
$E2/$ED/ { LOOP Delay ;Decrement millesecond count and loop}
{;}
{; Dropped through -- no character arrived in specified interval.}
{; Return TimeOut as result.}
{;}
$BB/>TIMEOUT/ { MOV BX,>TimeOut ;Pick up timeout value}
$C4/$7E/<C/ { LES DI,[BP+<C] ;Get result character address}
$26/$89/$1D/ { ES: MOV [DI],BX ;Store timeout value}
$E9/$3E/$00/ { JMP Return ;Return to caller}
{;}
{; Buffer not empty -- pick up next character.}
{;}
$C4/$3E/>ASYNC_BUFFER_PTR/ {Rec1: LES DI,[>Async_Buffer_Ptr] ;Pick up buffer address}
$01/$C7/ { ADD DI,AX ;Add character offset}
$26/$8A/$1D/ { ES: MOV BL,[DI] ;Get character from buffer}
{;}
$30/$FF/ { XOR BH,BH ;Clear high-order bits}
$C4/$7E/<C/ { LES DI,[BP+<C] ;Get result address}
$26/$89/$1D/ { ES: MOV [DI],BX ;Store character from buffer}
{;}
$40/ { INC AX ;Increment tail pointer}
$3B/$06/>ASYNC_BUFFER_SIZE/ { CMP AX,[>Async_Buffer_Size] ;Past end of buffer?}
$7E/$02/ { JLE Rec2 ;No -- skip wrapping}
$31/$C0/ { XOR AX,AX ;Yes -- point to start of buffer}
$A3/>ASYNC_BUFFER_TAIL/ {Rec2: MOV [>Async_Buffer_Tail],AX ;Update tail pointer}
$FF/$0E/>ASYNC_BUFFER_USED/ { DEC Word [>Async_Buffer_Used] ;Update buffer usage count}
{;}
{; If XOFF previously sent because buffer was too full, and}
{; now buffer is reasonably empty, send XON to get things rolling again.}
{;}
$F6/$06/>ASYNC_XOFF_SENT/$01/ { TEST BYTE [<Async_XOff_Sent],1 ;Check if Xoff sent}
$74/$16/ { JZ Return ;No -- skip.}
{;}
$A1/>ASYNC_BUFFER_USED/ { MOV AX,[>Async_Buffer_Used] ;Pick up amount of buffer used}
$3B/$06/>ASYNC_BUFFER_LOW/ { CMP AX,[>Async_Buffer_Low] ;Check if low enough}
$7F/$0D/ { JG Return ;Still too full, skip}
{;}
$B8/>XON/ { MOV AX,>XON ;Push XON onto stack}
$50/ { PUSH AX}
$FF/$1E/>ASYNC_SEND_ADDR/ { CALL FAR [>Async_Send_Addr] ;Call output routine}
{;}
$C6/$06/>ASYNC_XOFF_SENT/$00/ { MOV BYTE [>Async_XOff_Sent],0 ;Clear Xoff flag}
{;}
$80/$26/>ASYNC_LINE_STATUS/$FD);{Return: AND Byte [>Async_Line_Status],$FD ;Remove overflow flag}
END (* Async_Receive_With_Timeout *);
(*----------------------------------------------------------------------*)
(* Async_Stuff --- Stuff character into receive buffer *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Stuff( Ch: CHAR );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Stuff *)
(* *)
(* Purpose: Stuffs a character into receive buffer *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Stuff( Ch : Char ); *)
(* *)
(* Ch --- Character to stuff *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
VAR
New_Head : INTEGER;
BEGIN (* Async_Stuff *)
Async_Buffer_Ptr^[Async_Buffer_Head] := Ch;
New_Head := SUCC( Async_Buffer_Head ) MOD
SUCC( Async_Buffer_Size );
IF ( New_Head = Async_Buffer_Tail ) THEN
Async_Buffer_Overflow := TRUE
ELSE
BEGIN
Async_Buffer_Head := New_Head;
Async_Buffer_Used := SUCC( Async_Buffer_Used );
IF ( Async_Buffer_Used > Async_MaxBufferUsed ) THEN
Async_MaxBufferUsed := Async_Buffer_Used
END;
END (* Async_Stuff *);
(*----------------------------------------------------------------------*)
(* Async_Find_Delay --- Finds delay loop value for 1 millesecond delay *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Find_Delay( VAR One_MS_Delay : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Find_Delay *)
(* *)
(* Purpose: Finds loop count value to effect 1 ms delay *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Find_Delay( VAR One_MS_Delay : INTEGER ); *)
(* *)
(* One_MS_Delay --- Resulting loop count for 1 ms delay *)
(* *)
(* Using result: *)
(* *)
(* Use loop of form: *)
(* *)
(* MOV CX,[>One_MS_Delay] *)
(* Delay: LOOP Delay *)
(* *)
(* to delay for 1 ms. *)
(* *)
(* Remarks: *)
(* *)
(* This routine watches over the CPU elapsed timer value for *)
(* just one timer interval (55 milleseconds). During that time *)
(* we run a tight loop and accumulate the ticks. The result *)
(* is the number of ticks required for a 55 ms delay. The *)
(* ticks for a 1 ms delay = ( ticks for 55 ms ) / 55. *)
(* *)
(* To avoid overflow problems on fast machines, and to ease the *)
(* worry about storing results at the second timer tick, we *)
(* break up the single long tight loop into a series of short *)
(* loops inside an outer loop. We check if the timer has *)
(* expired at the end of each inner short loop. Then the *)
(* time for the 55 ms delay is: *)
(* *)
(* Ticks_for_55 := Inner_Ticks * Outer_Ticks; *)
(* *)
(* and the corresponding 1 ms delay is: *)
(* *)
(* Ticks_For_1 := Ticks_For_55 DIV 55; *)
(* *)
(* To simplify things, we choose the inner tick value to be *)
(* 2 x 55 = 110. Then: *)
(* *)
(* Ticks_For_1 := ( 110 * Outer_Ticks ) / 55; ==> *)
(* Ticks_For_1 := 2 * Outer_Ticks; *)
(* *)
(* The CPU timer is located in four bytes at $0000:$46C. *)
(* Interrupt $1A also returns these bytes, but using the *)
(* interrupt results in an inaccurate loop count value. *)
(* *)
(* Thanks to Brian Foley and Kim Kokonnen for help with this *)
(* problem. *)
(* *)
(*----------------------------------------------------------------------*)
(* STRUCTURED *) CONST
Hi_Timer : INTEGER = 0 (* Saves high portion of timer *);
Lo_Timer : INTEGER = 0 (* Saves low portion of timer *);
OutCount : INTEGER = 0 (* Accumulates outer loop counts *);
BEGIN (* Async_Find_Delay *)
INLINE(
{;}
$31/$C0/ { XOR AX,AX ;Clear AX to zero}
$8E/$C0/ { MOV ES,AX ;Allow low-memory access}
{;}
$C7/$06/>OUTCOUNT/$00/$00/ { MOV WORD [>OutCount],0 ;Clear outer loop counter}
{;}
$FA/ { CLI ;No interrupts while reading}
$26/$8B/$0E/>$46E/ { ES: MOV CX,[>$46E] ;Hi part of CPU timer value}
$26/$8B/$16/>$46C/ { ES: MOV DX,[>$46C] ;Lo part of CPU timer value}
$FB/ { STI ;Interrupts back on}
{;}
$89/$0E/>HI_TIMER/ { MOV [>Hi_Timer],CX ;Save hi part of timer}
$89/$16/>LO_TIMER/ { MOV [>Lo_Timer],DX ;Save low part of timer}
{;}
$FA/ {Loop1: CLI ;No interrupts while reading}
{;}
$26/$8B/$0E/>$46E/ { ES: MOV CX,[>$46E] ;Hi part of CPU timer value}
$26/$8B/$16/>$46C/ { ES: MOV DX,[>$46C] ;Lo part of CPU timer value}
{;}
$FB/ { STI ;Interrupts back on}
{;}
$89/$C8/ { MOV AX,CX ;Save CX and DX for later}
$89/$D3/ { MOV BX,DX}
{;}
$2B/$06/>HI_TIMER/ { SUB AX,[>Hi_Timer] ;Subtract low order part}
$1B/$1E/>LO_TIMER/ { SBB BX,[>Lo_Timer] ;Subtract high order part}
{;}
$74/$E6/ { JE Loop1 ;Continue until non-0 tick difference}
{;}
$89/$0E/>HI_TIMER/ { MOV [>Hi_Timer],CX ;Save hi part}
$89/$16/>LO_TIMER/ { MOV [>Lo_Timer],DX ;Save low part}
{;}
$B9/$6E/$00/ {Loop2: MOV CX,110 ;Run short delay loop.}
$E2/$FE/ {Delay: LOOP Delay}
{;}
$FA/ { CLI ;No interrupts while reading}
{;}
$26/$8B/$0E/>$46E/ { ES: MOV CX,[>$46E] ;Hi part of CPU timer value}
$26/$8B/$16/>$46C/ { ES: MOV DX,[>$46C] ;Lo part of CPU timer value}
{;}
$FB/ { STI ;Interrupts back on}
{;}
$FF/$06/>OUTCOUNT/ { INC WORD [>OutCount] ;Increment outer loop count}
{;}
$2B/$0E/>HI_TIMER/ { SUB CX,[>Hi_Timer] ;Subtract low order part}
$1B/$16/>LO_TIMER/ { SBB DX,[>Lo_Timer] ;Subtract high order part}
{;}
$74/$E1/ { JE Loop2 ;Keep going if next tick not found}
{;}
$A1/>OUTCOUNT/ { MOV AX,[>OutCount] ;Pick up outer loop counter}
$D1/$E0/ { SHL AX,1 ;* 2 = ticks for 1 ms delay}
{;}
$C4/$BE/>ONE_MS_DELAY/ { LES DI,[BP+>One_MS_Delay] ;Get address of result}
$26/$89/$05); { ES: MOV [DI],AX ;Store result}
END (* Async_Find_Delay *);
(*----------------------------------------------------------------------*)
(* Async_Init --- Initialize Asynchronous Variables *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Init( Async_Buffer_Max : INTEGER;
Async_OBuffer_Max : INTEGER;
Async_High_Lev1 : INTEGER;
Async_High_Lev2 : INTEGER;
Async_Low_Lev : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Init *)
(* *)
(* Purpose: Initializes variables *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Init( Async_Buffer_Max : INTEGER; *)
(* Async_OBuffer_Max : INTEGER; *)
(* Async_High_Lev1 : INTEGER; *)
(* Async_High_Lev2 : INTEGER; *)
(* Async_Low_Lev : INTEGER ); *)
(* *)
(* Calls: Async_Find_Delay *)
(* TurnOffTimeSharing *)
(* TurnOnTimeSharing *)
(* *)
(*----------------------------------------------------------------------*)
VAR
I: INTEGER;
(*----------------------------------------------------------------------*)
begin
(* No port open yet. *)
Async_Open_Flag:=FALSE;
(* No XON/XOFF handling yet. *)
Async_XOFF_Sent:=FALSE;
Async_XOFF_Received:=FALSE;
Async_XOFF_Rec_Display:=FALSE;
Async_XON_Rec_Display:=FALSE;
Async_Send_XOFF:=FALSE;
(* Set up empty receive buffer *)
Async_Buffer_Overflow:=FALSE;
Async_Buffer_Used:=0;
Async_MaxBufferUsed:=0;
Async_Buffer_Head:=0;
Async_Buffer_Tail:=0;
(* Set up empty send buffer. *)
Async_OBuffer_Overflow:=FALSE;
Async_OBuffer_Used:=0;
Async_MaxOBufferUsed:=0;
Async_OBuffer_Head:=0;
Async_OBuffer_Tail:=0;
(* Set default wait time for output *)
(* buffer to drain when it fills up. *)
Async_Output_Delay:=500;
(* No modem or line errors yet. *)
Async_Line_Status:=0;
Async_Modem_Status:=0;
Async_Line_Error_Flags:=0;
(* Get buffer sizes *)
if (Async_Buffer_Max>0) then Async_Buffer_Size:=Async_Buffer_Max-1
else Async_Buffer_Size:=4095;
if (Async_OBuffer_Max>0) then Async_OBuffer_Size:=Async_OBuffer_Max-1
else Async_OBuffer_Size:=1131;
(* Get receive buffer overflow *)
(* check-points. *)
if (Async_Low_Lev>0) then Async_Buffer_Low:=Async_Low_Lev
else Async_Buffer_Low:=Async_Buffer_Size div 4;
if (Async_High_Lev1>0) then Async_Buffer_High:=Async_High_Lev1
else Async_Buffer_High:=(Async_Buffer_Size div 4)*3;
if (Async_High_Lev2>0) then Async_Buffer_High_2:=Async_High_Lev2
else Async_Buffer_High_2:=(Async_Buffer_Size div 10)*9;
(* Allocate buffers *)
(* getmem(Async_Buffer_Ptr,Async_Buffer_Size+1);
getmem(Async_OBuffer_Ptr,Async_OBuffer_Size+1);*)
(* No UART addresses defined yet *)
(*procedure Async_Init;
begin
Async_Open_Flag := FALSE;
Async_Buffer_Overflow := FALSE;
Async_Buffer_Used := 0;
Async_MaxBufferUsed := 0;
end;*)
Async_Uart_IER:=0;
Async_Uart_IIR:=0;
Async_Uart_MSR:=0;
Async_Uart_LSR:=0;
(* Set default port addresses *)
(* and default IRQ lines *)
for i:=1 to MaxComPorts do begin
com_base[i]:=default_com_base[i];
com_irq[i]:=default_com_irq[i];
end;
(* Get the delay loop value for 1 ms *)
(* delay loops. *)
(* ---- You should turn off time sharing if running under a multitasker *)
(* ---- to get an accurate delay loop value. If MTASK is $DEFINEd, *)
(* ---- then the calls to the PibMDos routines for interfacing with *)
(* ---- multitaskers will be generated. *)
{$IFDEF MTASK}
if (timesharingactive) then turnofftimesharing;
{$ENDIF}
(* Async_find_delay(Async_onemsdelay );*)
{$IFDEF MTASK}
if (timesharingactive) then turnontimesharing;
{$ENDIF}
end;
(*----------------------------------------------------------------------*)
(* Async_Carrier_Drop --- Check for modem carrier drop/timeout *)
(*----------------------------------------------------------------------*)
function Async_carrier_drop:boolean;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Carrier_Drop *)
(* *)
(* Purpose: Looks for modem carrier drop/timeout *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Carrier_Drop : BOOLEAN; *)
(* *)
(* Flag is set TRUE if carrier dropped, else FALSE. *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
begin
Async_carrier_drop:=not (odd(port[UART_MSR+Async_base] shr 7) or
Async_hard_wired_on);
end;
(*----------------------------------------------------------------------*)
(* Async_Line_Error --- Check if line status error occurred *)
(*----------------------------------------------------------------------*)
function Async_line_error(var error_flags:byte):boolean;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Line_Error *)
(* *)
(* Purpose: Check if line status error occurred *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Line_Error(VAR Error_Flags: BYTE): BOOLEAN; *)
(* *)
(* Error_Flags --- Current error flags *)
(* *)
(* Flag returned TRUE if line status error occurred, *)
(* Flag returned FALSE if no error. *)
(* *)
(* Calls: None *)
(* *)
(* Remarks: *)
(* *)
(* The line status error flag is cleared here. *)
(* *)
(*----------------------------------------------------------------------*)
begin
Async_line_error:=(Async_line_error_flags<>0);
Error_flags:=Async_line_error_flags;
Async_line_error_flags:=0;
end;
(*----------------------------------------------------------------------*)
(* Async_Ring_Detect --- Check for phone ringing *)
(*----------------------------------------------------------------------*)
function Async_ring_detect:boolean;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Ring_Detect *)
(* *)
(* Purpose: Looks for phone ringing *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Ring_Detect : BOOLEAN; *)
(* *)
(* Flag is set TRUE if ringing detected, else FALSE. *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
begin
Async_ring_detect:=odd(port[UART_MSR+Async_base] shr 6);
end;
(*----------------------------------------------------------------------*)
(* Async_Send_Break --- Send break (attention) signal *)
(*----------------------------------------------------------------------*)
procedure Async_send_break;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Send_Break *)
(* *)
(* Purpose: Sends break signal over communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_Break; *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
var break_lcr,old_lcr:byte;
begin
Old_Lcr := Port[ UART_LCR + Async_Base ];
Break_Lcr := Old_Lcr;
IF Break_Lcr > 127 THEN Break_Lcr := Break_Lcr - 128;
IF Break_Lcr <= 63 THEN Break_Lcr := Break_Lcr + 64;
Port[ UART_LCR + Async_Base ] := Break_Lcr;
DELAY( Async_Break_Length * 10 );
Port[ UART_LCR + Async_Base ] := Old_Lcr;
end;
(*----------------------------------------------------------------------*)
(* Async_Send_String --- Send string over communications port *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Send_String( S : AnyStr );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Send_String *)
(* *)
(* Purpose: Sends string out over communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_String( S : AnyStr ); *)
(* *)
(* S --- String to send *)
(* *)
(* Calls: Async_Send *)
(* *)
(*----------------------------------------------------------------------*)
var i:integer;
begin
for i:=1 to length(s) do Async_send(s[i]);
end;
(*----------------------------------------------------------------------*)
(* Async_Send_String_With_Delays --- Send string with timed delays *)
(*----------------------------------------------------------------------*)
procedure Async_send_string_with_delays(s:anystr;
char_delay:integer;
eos_delay:integer);
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Send_String_With_Delays *)
(* *)
(* Purpose: Sends string out over communications port with *)
(* specified delays for each character and at the *)
(* end of the string. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Send_String_With_Delays( S : AnyStr ; *)
(* Char_Delay : INTEGER; *)
(* EOS_Delay : INTEGER ); *)
(* *)
(* S --- String to send *)
(* Char_Delay --- Number of milliseconds to delay after *)
(* sending each character *)
(* EOS_Delay --- Number of milleseconds to delay after *)
(* sending last character in string *)
(* *)
(* Calls: Async_Send *)
(* Async_Send_String *)
(* Length *)
(* Delay *)
(* *)
(* Remarks: *)
(* *)
(* This routine is useful when writing routines to perform *)
(* non-protocol uploads. Many computer systems require delays *)
(* between receipt of characters for correct processing. The *)
(* delay for end-of-string usually applies when the string *)
(* represents an entire line of a file. *)
(* *)
(* If delays are not required, Async_Send_String is faster. *)
(* This routine will call Async_Send_String is no character *)
(* delay is to be done. *)
(* *)
(*----------------------------------------------------------------------*)
var i:integer;
begin
if (char_delay<=0) then
Async_send_string(s)
else
for i:=1 to length(s) do begin
Async_send(s[i]);
delay(char_delay);
end;
if (eos_delay>0) then delay(eos_delay);
end;
(*----------------------------------------------------------------------*)
(* Async_Percentage_Used --- Report Percentage Buffer Filled *)
(*----------------------------------------------------------------------*)
function Async_percentage_used:real;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Percent_Used *)
(* *)
(* Purpose: Reports percentage of com buffer currently filled *)
(* *)
(* Calling Sequence: *)
(* *)
(* Percentage := Async_Percentage_Used : Real; *)
(* *)
(* Percentage gets how much of buffer is filled; *)
(* value goes from 0.0 (empty) to 1.0 (totally full). *)
(* *)
(* Calls: None *)
(* *)
(* Remarks: *)
(* *)
(* This routine is helpful when incorporating handshaking into *)
(* a communications program. For example, assume that the host *)
(* computer uses the XON/XOFF (DC1/DC3) protocol. Then the *)
(* PC program should issue an XOFF to the host when the value *)
(* returned by Async_Percentage_Used > .75 or so. When the *)
(* utilization percentage drops below .25 or so, the PC program *)
(* should transmit an XON. *)
(* *)
(*----------------------------------------------------------------------*)
begin
Async_percentage_used:=Async_buffer_used/(Async_buffer_size+1);
end;
(*----------------------------------------------------------------------*)
(* Async_Peek --- Peek ahead in communications buffer *)
(*----------------------------------------------------------------------*)
function Async_peek(nchars:integer):char;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Peek *)
(* *)
(* Purpose: Peeks ahead in comm buffer *)
(* *)
(* Calling Sequence: *)
(* *)
(* Ch := Async_Peek( NChars: INTEGER) : CHAR; *)
(* *)
(* NChars --- # of characters to peek ahead *)
(* Ch --- returned (peeked) character *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
var i:integer;
begin
i:=(Async_buffer_tail+nchars) mod Async_buffer_size;
if (i>Async_buffer_head) then
Async_peek := #0
else
Async_peek := Async_buffer_ptr^[i];
end;
(*----------------------------------------------------------------------*)
(* Async_Setup_Port --- Setup port address and IRQ line *)
(*----------------------------------------------------------------------*)
procedure Async_setup_port(comport,base_address:integer;
irq_line:integer);
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Setup_Port *)
(* *)
(* Purpose: Sets up port address and IRQ line *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Setup_Port( ComPort : INTEGER; *)
(* Base_Address : INTEGER; *)
(* IRQ_Line : INTEGER ); *)
(* *)
(* ComPort --- which port (1 though MaxComPorts) *)
(* Base_Address --- Base address of port. If -1, then *)
(* standard default address used. *)
(* IRQ_Line --- IRQ line for interrupts for port. If -1, *)
(* then standard default address used. *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
var port_offset:integer;
begin
if ((comport>0) and (comport<=maxcomports)) then begin
if (base_address=-1) then
base_address:=default_com_base[comport];
if (irq_line=-1) then
irq_line:=default_com_irq[comport];
com_base[comport]:=base_address;
com_irq[comport]:=irq_line;
port_offset:=RS232_base+(pred(comport) shl 1);
memw[$0000:port_offset]:=base_address;
end;
end;
(*----------------------------------------------------------------------*)
(* Async_Release_Buffers --- Release buffers for serial ports *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* Async_Drain_Output_Buffer --- Wait for output buffer to drain *)
(*----------------------------------------------------------------------*)
procedure Async_drain_output_buffer(max_wait_time:integer);
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Drain_Output_Buffer; *)
(* *)
(* Purpose: Waits for output buffer to drain. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Drain_Output_Buffer( Max_Wait_Time : INTEGER ); *)
(* *)
(* Max_Wait_Time --- Maximum # of seconds to wait for *)
(* output buffer to drain. *)
(* *)
(* Calls: TimeOfDay *)
(* TimeDiff *)
(* GiveAwayTime *)
(* *)
(*----------------------------------------------------------------------*)
var t1:longint;
begin
t1:=timeofday;
while ((Async_obuffer_head<>Async_obuffer_tail) and
(timediff(t1,timeofday)<=max_wait_time)) do
{$IFDEF MTASK}
giveawaytime(1);
{$ELSE}
;
{$ENDIF}
end;
(*----------------------------------------------------------------------*)
(* Async_Port_Address_Given --- Check if port address in memory *)
(*----------------------------------------------------------------------*)
FUNCTION Async_Port_Address_Given( Com_Port : INTEGER ) : BOOLEAN;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Port_Address_Given; *)
(* *)
(* Purpose: Checks if port address in memory. *)
(* *)
(* Calling Sequence: *)
(* *)
(* There := Async_Port_Address_Given( Com_Port : INTEGER ) : *)
(* BOOLEAN; *)
(* *)
(* Com_Port --- Port to check (1 through MaxComPorts) *)
(* There --- TRUE if port address in memory. *)
(* *)
(*----------------------------------------------------------------------*)
VAR
Port_Offset : INTEGER;
BEGIN (* Async_Port_Address_Given *)
IF ( ( Com_Port > 0 ) AND ( Com_Port < MaxComPorts ) ) THEN
BEGIN
Port_Offset := RS232_Base + ( PRED( Com_Port ) SHL 1 );
Async_Port_Address_Given := ( MemW[$0:Port_Offset] <> 0 );
END
ELSE
Async_Port_Address_Given := FALSE;
END (* Async_Port_Address_Given *);
(*----------------------------------------------------------------------*)
(* Async_Flush_Output_Buffer --- Flush output buffer for serial port *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Flush_Output_Buffer;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Flush_Output_Buffer; *)
(* *)
(* Purpose: Flushes output buffer for serial port. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Flush_Output_Buffer; *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
BEGIN (* Async_Flush_Output_Buffer *)
Async_OBuffer_Head := Async_OBuffer_Tail;
Async_OBuffer_Used := 0;
END (* Async_Flush_Output_Buffer *);
procedure async_close(drop_dtr:boolean);
var i,m:integer;
begin
if (Async_Open_Flag) then begin
{ disable the IRQ on the 8259 }
inline($FA);
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 + async_base] := 0;
{ disable OUT2 on the 8250 }
if (drop_dtr) then
Port[UART_MCR + async_base] := 0
else
Port[UART_MCR + async_base] := 1;
inline($FB);
SetIntVec(Async_Irq + 8,Async_save_iaddr);
{ re-initialize our data areas so we know the port is closed }
Async_Open_Flag := FALSE;
end;
END (* Async_Close *);
(*----------------------------------------------------------------------*)
(* Async_Clear_Errors --- Reset pending errors in async port *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Clear_Errors;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Clear_Errors *)
(* *)
(* Purpose: Resets pending errors in async port *)
(* *)
(* Calling sequence: *)
(* *)
(* Async_Clear_Errors; *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
var i,m:integer;
begin
(* Read the RBR and reset any pending error conditions. *)
(* First turn off the Divisor Access Latch Bit to allow *)
(* access to RBR, etc. *)
inline($FA); (* disable interrupts *)
port[UART_LCR+Async_Base]:=port[UART_LCR+Async_Base] and $7F;
(* Read the Line Status Register to reset any errors *)
(* it indicates *)
i:=port[UART_LSR+Async_Base];
(* Read the Receiver Buffer Register in case it *)
(* contains a character *)
i:=port[UART_RBR+Async_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 OUT2 on 8250 *)
i:=port[UART_MCR+Async_Base];
port[UART_MCR+Async_Base]:=i or $0B;
(* enable the data ready interrupt on the 8250 *)
port[UART_IER+Async_Base]:=$0F;
(* Re-enable 8259 *)
port[$20] := $20;
inline($FB); (* enable interrupts *)
end;
(*----------------------------------------------------------------------*)
(* Async_Reset_Port --- Set/reset communications port parameters *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Reset_Port( ComPort : INTEGER;
BaudRate : WORD;
Parity : CHAR;
WordSize : INTEGER;
StopBits : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Reset_Port *)
(* *)
(* Purpose: Resets communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Reset_Port( ComPort : INTEGER; *)
(* BaudRate : WORD; *)
(* Parity : CHAR; *)
(* WordSize : INTEGER; *)
(* StopBits : INTEGER); *)
(* *)
(* ComPort --- which port (1, 2, 3, 4) *)
(* BaudRate --- Baud rate (110 to 38400) *)
(* Parity --- "E" for even, "O" for odd, "N" for none, *)
(* "M" for mark, "S" for space.
(* WordSize --- Bits per character (5 through 8) *)
(* StopBits --- How many stop bits (1 or 2) *)
(* *)
(* Calls: *)
(* *)
(* Async_Clear_Errors --- Clear async line errors *)
(* *)
(*----------------------------------------------------------------------*)
CONST (* Baud Rate Constants *)
Async_Num_Bauds = 10;
Async_Baud_Table : ARRAY [1..Async_Num_Bauds] OF RECORD
Baud, Bits : WORD;
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 ),
( Baud: 19200; Bits: $E0 ),
( Baud: 38400; Bits: $E0 ) );
VAR
I : INTEGER;
M : INTEGER;
ComParm : INTEGER;
BEGIN (* Async_Reset_Port *)
(*---------------------------------------------------*)
(* Build the ComParm for RS232_Init *)
(* See Technical Reference Manual for description *)
(*---------------------------------------------------*)
(* Set up the bits for the baud rate *)
IF ( BaudRate > Async_Baud_Table[Async_Num_Bauds].Baud ) THEN
BaudRate := Async_Baud_Table[Async_Num_Bauds].Baud
ELSE IF ( BaudRate < Async_Baud_Table[1].Baud ) THEN
BaudRate := Async_Baud_Table[1].Baud;
(* Remember baud rate for purges *)
Async_Baud_Rate := BaudRate;
I := 0;
REPEAT
I := I + 1
UNTIL ( ( I >= Async_Num_Bauds ) OR
( BaudRate = Async_Baud_Table[I].Baud ) );
ComParm := Async_Baud_Table[I].Bits;
(* Choose Parity. Temporarily *)
(* consider mark, space as none. *)
Parity := UpCase( Parity );
CASE Parity OF
'E' : ComParm := ComParm OR $0018;
'O' : ComParm := ComParm OR $0008;
ELSE ;
END (* CASE *);
(* Choose number of data bits *)
WordSize := WordSize - 5;
IF ( WordSize < 0 ) OR ( WordSize > 3 ) THEN
WordSize := 3;
ComParm := ComParm OR WordSize;
(* Choose stop bits *)
IF StopBits = 2 THEN
ComParm := ComParm OR $0004; (* default is 1 stop bit *)
(* Use the BIOS COM port init routine *)
BIOS_RS232_Init( ComPort - 1 , ComParm );
(* If > 9600 baud, we have to screw *)
(* around a bit *)
IF ( ( BaudRate = 19200 ) OR ( BaudRate = 38400 ) ) THEN
BEGIN
I := PORT[ UART_LCR + Async_Base ];
PORT[ UART_LCR + Async_Base ] := I OR $80;
IF ( BaudRate = 19200 ) THEN
PORT[ UART_THR + Async_Base ] := 6
ELSE
PORT[ UART_THR + Async_Base ] := 3;
PORT[ UART_IER + Async_Base ] := 0;
I := PORT[ UART_LCR + Async_Base ];
PORT[ UART_LCR + Async_Base ] := I AND $7F;
END;
(* Now fix up mark, space parity *)
IF ( ( Parity = 'M' ) OR ( Parity = 'S' ) ) THEN
BEGIN
I := PORT[ UART_LCR + Async_Base ];
PORT[ UART_LCR + Async_Base ] := $80;
ComParm := WordSize OR ( ( StopBits - 1 ) SHL 2 );
CASE Parity OF
'M' : ComParm := ComParm OR $0028;
'S' : ComParm := ComParm OR $0038;
ELSE ;
END (* CASE *);
PORT[ UART_LCR + Async_Base ] := ComParm;
END;
(* Clear any pending errors on *)
(* async line *)
Async_Clear_Errors;
end;
procedure async_release_buffers;
begin
if (async_open_flag) then async_close(FALSE);
(* freemem(async_buffer_ptr,async_buffer_size+1);
freemem(async_obuffer_ptr,async_obuffer_size+1);*)
end;
end.