home *** CD-ROM | disk | FTP | other *** search
- {----------------------------------------------------------------------------
- Interr is a program written to demonstrate the use of both
- interrupt routines and com port communication. There are some inline
- instructions that are used in the interrupt routine that do not appear in
- the Turbo manual. This is because the Turbo manual contains incomplete
- inline coding. When making your own interrupt handlers be sure to save
- all registers, as has been done in this example. There is also a
- restoration of the DS register in the handler. When an interrupt occurs
- all segment registers are set to the code segment of the interrupt routine
- this disallows the use of global variables, because of a destroyed DS
- register. To counter act this affect, the DS is restored via an absolute
- variable "segment".
-
- SOURCE 'DUMTRM.PAS' FROM DL 1
-
- Written by,
- Jim McCarthy
- Technical Support
- Borland International
-
- and
- Andy Batony
- Teleware Incorporated
-
- MODIFIED FOR XON/XOFF SUPPORT, fixed transmitt bug, added
- queue handling code and other support functions.
-
- Gary Miller
- Perception Technology Corp.
-
- -----------------------------------------------------------------------------}
-
- CONST
- irq4 = $30; { Interrupt vector address for }
- { COM1. }
- irq3 = $2C; { Vector for COM2. }
- com1base = $03F8; { Port address of COM1. }
- com2base = $02F8; { Port address of COM2. }
- { Offset to add to com#base for}
- intenreg = 1; { Interrupt enable register }
- intidreg = 2; { Interrupt id register }
- linectrl = 3; { Line control register }
- modemctrl = 4; { Modem control register }
- linestat = 5; { Line status register }
- modemstat = 6; { Modem status register }
- eoi = $20; { End of interrupt command }
- queue_size=1024; { queue size, set to any value }
- max_queue_depth=768; { critical point for xoff }
- Xoff=$13; { xoff byte }
- Xon=$11; { xon byte }
- CXon=#$11; { xon character }
- CXoff=#$13; { xoff character }
- MaxWait=200; { maximum wait while xoffed }
-
- TYPE { Type declarations }
- ratetype = (rate300,rate1200,rate4800,rate9600);
- bitsperchartype = (_8bits,_7bits,_6bits,_5bits,Nobits);
- paritytype = (even,odd,none,whoknows);
- stopbittype = (onestopbit,twostopbit,nostopbit);
- comtype = (com1,com2);
- Busy = Array [comtype] of boolean;
- bytechar = record case boolean of
- true :(b:byte);
- false:(c:char)
- end;
-
- queue_value = bytechar;
- queue_type = record
- f,r : integer; { front, rear pointers }
- DepthFlag : boolean; { true if input XOFFed }
- err : boolean; { queue error, full ect }
- data : array [0..queue_size] of queue_value;
- end;
-
- regrec = record
- ax,bx,cx,dx,bp,di,si,ds,es,flags : integer;
- end;
- Str255 = String[255];
-
-
- VAR
- segment : integer absolute cseg:$00A0; { Address for storing DS }
- com1vecseg, { Segment of DOS set }
- com1vecoff, { Offset of DOS set com int. }
- com2vecseg, { Segment of DOS set }
- com2vecoff, { Offset of DOS set com int. }
- comport : integer; { Comport address }
- tbyte,
- lbyte : integer; { global var for interrupt }
- comp : comtype; { Used to specify which comport}
- registers : regrec; { Registers used in DOS call }
- ComBusy : Busy; { Comport trans busy flags }
- GPPqueue,
- com1queue,
- com2queue : queue_type; { input queue }
- OutputXoffed : array [comtype] of boolean;{XOFFed flag }
- com1_OverRun,
- com2_OverRun : boolean; { Queue overrun flags }
-
- {----------------------------------------------------------------------------
- This is the interrupt handler for the COM1 or COM2 comports. Notice
- the restoration of the DS register through a move to the AX from address
- CS:00A0. The absolute variable "segment" is initialized at the begining
- of the program to contain the value of "DSEG". The inline statments should
- replace the current ones in the Turbo reference manual.
- ----------------------------------------------------------------------------}
-
- PROCEDURE IntCOM1_Handler;
-
- BEGIN
- inline( $50 { push ax }
- /$53 { push bx }
- /$51 { push cx }
- /$52 { push dx } { Save all the registers }
- /$57 { push di }
- /$56 { push si }
- /$06 { push es }
- /$1E { push ds }
- /$2E { cs: }
- /$A1 /$A0 /$00 { mov ax, [00A0] } { Get the Current data }
- /$50 { push ax } { segment }
- /$1F { pop ds } ); { Restore the DS register }
- lbyte := port[ com1base + intidreg ]; { Get Interrupt ID }
- lbyte := ( lbyte shr 1 ) and $03; { Isolate ID bits }
- If lbyte = 1 then { Check for Transmit done }
- ComBusy[com1]:=false { Clear busy flag }
- else { }
- If lbyte = 2 then { Check for Received Data }
- begin { process the data }
- tbyte := port[ com1base ]; { Get the character in the port}
- lbyte := port[ com1base + linestat ];{ Get the status of the port }
- If (tbyte=Xon) or (tbyte=Xoff) then
- begin
- If tbyte=Xon then OutputXoffed[com1]:=false
- else OutputXoffed[com1]:=true;
- end
- else
- If ((com1queue.r+1) mod queue_size)=com1queue.f then
- COM1_OverRun:=true
- else
- begin
- com1queue.r:=(com1queue.r+1) mod queue_size;
- com1queue.data[com1queue.r].b:=tbyte;
- if not com1queue.DepthFlag then
- if ((com1queue.r-com1queue.f) mod queue_size) >= max_queue_depth then
- begin
- repeat
- tbyte:=port[com1base+linestat];
- Until (tbyte and $20)=$20;
- port[com1base]:=Xoff; { Output the character }
- com1queue.DepthFlag:=true;
- end;
- end;
- end; { lbyte = 2 }
- port[$20] := eoi; { signal end of interrupt code }
- inline( $1F { pop ds }
- /$07 { pop es }
- /$5E { pop si }
- /$5F { pop di }
- /$5A { pop dx }
- /$59 { pop cx } { Restore all registers }
- /$5B { pop bx }
- /$58 { pop ax }
- /$5D { pop bp } { Reset the stack to its }
- /$89 /$EC { mov sp,bp } { proper position }
- /$5D { pop bp }
- /$CF ); { iret } { Return }
- END;
-
- PROCEDURE IntCOM2_Handler;
-
- BEGIN
- inline( $50 { push ax }
- /$53 { push bx }
- /$51 { push cx }
- /$52 { push dx } { Save all the registers }
- /$57 { push di }
- /$56 { push si }
- /$06 { push es }
- /$1E { push ds }
- /$2E { cs: }
- /$A1 /$A0 /$00 { mov ax, [00A0] } { Get the Current data }
- /$50 { push ax } { segment }
- /$1F { pop ds } ); { Restore the DS register }
- lbyte := port[ com2base + intidreg ]; { Get Interrupt ID }
- lbyte := ( lbyte shr 1 ) and $03; { Isolate ID bits }
- If lbyte = 1 then { Check for Transmit done }
- ComBusy[com2]:=false { Clear busy flag }
- else { }
- If lbyte = 2 then { Check for Received Data }
- begin { process the data }
- tbyte := port[ com2base ]; { Get the character in the port}
- lbyte := port[ com2base + linestat ];{ Get the status of the port }
- If (tbyte=Xon) or (tbyte=Xoff) then
- begin
- If tbyte=Xon then OutputXoffed[com2]:=false
- else OutputXoffed[com2]:=true;
- end
- else
- If ((com2queue.r+1) mod queue_size)=com2queue.f then
- COM2_OverRun:=true
- else
- begin
- com2queue.r:=(com2queue.r+1) mod queue_size;
- com2queue.data[com2queue.r].b:=tbyte;
- if not com2queue.DepthFlag then
- if ((com2queue.r-com2queue.f) mod queue_size) >= max_queue_depth then
- begin
- repeat
- tbyte:=port[com1base+linestat];
- Until (tbyte and $20)=$20;
- port[com2base]:=Xoff; { Output the character }
- com2queue.DepthFlag:=true;
- end;
- end;
- end; { lbyte = 2 }
-
- port[$20] := eoi; { signal end of interrupt code }
- inline( $1F { pop ds }
- /$07 { pop es }
- /$5E { pop si }
- /$5F { pop di }
- /$5A { pop dx }
- /$59 { pop cx } { Restore all registers }
- /$5B { pop bx }
- /$58 { pop ax }
- /$5D { pop bp } { Reset the stack to its }
- /$89 /$EC { mov sp,bp } { proper position }
- /$5D { pop bp }
- /$CF ); { iret } { Return }
- END;
-
- {-----------------------------------------------------------------------------
- This procedure sets the baud rate of the comport to either 300, 1200,
- 4800, or 9600 baud. The communications protolcol parameters are also set
- to the values passed. The Divisor latches are set according to the table
- the IBM hardware technical reference manual ( p. 1-238 ).
- -----------------------------------------------------------------------------}
-
- PROCEDURE SetCom(comport:integer;
- r:ratetype;
- b:bitsperchartype;
- s:stopbittype;
- p:paritytype);
-
- CONST
- bits5=0;
- bits6=1;
- bits7=2;
- bits8=3;
- stopbit1=0; { These are constants used }
- stopbit2=4; { to define parity, stop bits, }
- noparity=0; { data bits, etc. }
- oddparity=8;
- evenparity=16;
-
- VAR
- tlcr, { Line control register }
- tdlmsb, { Divisor latch MSB }
- tdllsb : byte; { Divisor latch LSB }
- bits : integer; { No of bits per char }
- stopbits : integer; { No of stop bits per char }
- setparity : integer; { parity mode even, odd , none }
-
- BEGIN
- tdlmsb:=0; { Set DL MSB to 0 for 1200, }
- { 4800 and 9600 baud }
- case r of { Use case to check baud rate }
- rate300 : begin { Check for 300 baud }
- tdlmsb:=1; { Set DL MSB to 01 }
- tdllsb:=$80; { Set DL LSB to 80 }
- end; { for a total of 0180 }
- rate1200 : tdllsb:=$60; { 1200 set LSB to 60 }
- rate4800 : tdllsb:=$18; { 4800 set LSB to 18 }
- rate9600 : tdllsb:=$0c; { 0C for 9600 baud }
- end;
- tlcr := port[ comport ]; { Read the ports to clear any }
- tlcr := port[ comport + linestat ]; { error conditions }
- case p of { use case to check parity }
- even : setparity:=evenparity; { set for even parity }
- odd : setparity:=oddparity; { set for odd parity }
- none : setparity:=noparity; { set for no parity }
- else
- setparity:=noparity; { default is no parity }
- end;
- case s of { use case for stopbits }
- onestopbit : stopbits:=stopbit1; { one stopbit }
- twostopbit : stopbits:=stopbit2; { two stopbits }
- else
- stopbits:=stopbit1; { default to 1 stopbit }
- end;
- case b of { use case for bits per char }
- _8bits : bits:=bits8; { set to eight bits }
- _7bits : bits:=bits7; { set to seven bits }
- _6bits : bits:=bits6; { set to six bits }
- _5bits : bits:=bits5; { set to five bits }
- else
- bits:=bits8; { default to eight bits }
- end;
- port[ comport + linectrl ] := bits + stopbits + setparity;
- { Set the protocall }
- tlcr:=port[comport+linectrl]; { Get the Line control register}
- port[comport+linectrl]:=tlcr or $80; { Set Divisor Latch Access Bit }
- port[comport]:=tdllsb; { in order to access divisor }
- port[comport+1]:=tdlmsb; { latches, then store the }
- { values for the desired baud }
- { rate }
- port[comport+linectrl]:=tlcr and $7f; { then clear the DLAB in order }
- { to access to the receiver }
- { buffer }
- END;
-
- {-------------------------------------------------------------------------
- The procedure IntOn sets up the interrupt handler vectors, and
- communication protocal.
- -------------------------------------------------------------------------}
-
- PROCEDURE IntOn(com:comtype);
-
- CONST
- dtrtrue=1;
- rtstrue=2;
- bit3true=8;
-
- VAR
- tbyte : byte; { Temperary byte buffer }
- comport : integer; { Selected com address }
-
- BEGIN
- case com of
- com1:comport:=com1base; { Set the com port to talk to }
- com2:comport:=com2base;
- end;
- tbyte := port[ comport ]; { Read the ports to clear any }
- tbyte := port[ comport + linestat ]; { error conditions }
- port[ comport + modemctrl ] := dtrtrue + rtstrue + bit3true;
- port[ comport + intenreg ] := 3; { Enable com port interrupts }
- tbyte := port[$21]; { }
- with registers do
- begin
- ax:=$2500; { Load the function number for }
- { redefining an interrupt }
- ds:=cseg; { Get and set the segment and }
- case com of
- com1:dx:=ofs(IntCOM1_Handler); { offset of the handler }
- com2:dx:=ofs(IntCOM2_Handler); { COM1 or COM2 }
- end;
- end;
- case com of
- com1: begin
- com1vecoff:=memw[0000:irq4]; { Save the segment and offset }
- com1vecseg:=memw[0000:irq4+2]; { of the DOS interrupt handler }
- registers.ax:=registers.ax+$0c;{ Use the COM1: interrupt }
- intr($21,registers); { Call DOS to reset INT 0C }
- port[$21]:=tbyte and $ef; { }
- end;
- com2: begin
- com2vecoff:=memw[0000:irq3]; { Same as above }
- com2vecseg:=memw[0000:irq3+2]; { Same as above }
- registers.ax:=registers.ax+$0b;{ Use the COM2: interrupt }
- intr($21,registers); { Call DOS }
- port[$21]:=tbyte and $f7; { }
- end;
- end;
- ComBusy[com]:=false; { Com port not busy }
- inline($fb); { Enable interrupts }
- END;
-
- {-----------------------------------------------------------------------------
- This procedure restores the original system values to what they
- were before the interrupt handler was set into action.
- -----------------------------------------------------------------------------}
-
- PROCEDURE IntOff(com:comtype);
-
- VAR
- tbyte:byte;
-
- BEGIN
- inline($FA); { CLI } { Disable interrupts }
- tbyte:=port[$21]; { }
- case com of
- com1:comport:=com1base; { Set the com port to talk to }
- com2:comport:=com2base;
- end;
- port[comport+intenreg]:=0; { Disable COM interrupts }
- If comport=com1base then { If using COM1: then }
- begin
- port[$21]:=tbyte or $10; { }
- memw[0000:irq4]:=com1vecoff; { Restore the DOS interrupt }
- memw[0000:irq4+2]:=com1vecseg; { handler }
- end
- else
- begin
- memw[0000:irq3]:=com2vecoff; { Restore the DOS interrupt }
- memw[0000:irq3+2]:=com2vecseg; { handler }
- port[$21]:=tbyte or $08; { }
- end;
- ComBusy[com]:=true;
- END;
-
- {----------------------------------------------------------------------------
- This procedure outputs directly to the communications port the byte
- equivilent of the character to be sent.
- ----------------------------------------------------------------------------}
-
- PROCEDURE WriteCom( com : comtype; ch : char );
-
- VAR
- tbyte:byte;
- comport:integer;
-
- BEGIN
- case com of
- com1:comport:=com1base; { Set the com port to talk to }
- com2:comport:=com2base;
- end;
- while ComBusy[com] do
- begin
- end;
- inline($FA); { Disable interrupts }
- tbyte:=ord(ch); { Change to byte format }
- port[comport]:=tbyte; { Output the character }
- ComBusy[com]:=true; { Signal comport busy, transmitting }
- inline($FB); { Enable interrupts }
- END;
-
- { ================================================================= }
- { F u n c t i o n / P r o c e d u r e S e c t i o n }
- { ================================================================= }
-
- function depth(var q:queue_type):integer;
- { this function returns the depth of the queue }
- begin
- depth:=(q.r-q.f) mod queue_size
- end;
-
- function full(var q:queue_type):boolean;
- { this fuction tests to see it the queue is full }
- begin
- full:=q.r=(q.f-1) mod queue_size
- end;
-
- function empty(var q:queue_type):boolean;
- { this function test to see if the queue is empty }
- begin
- empty:=q.r=q.f
- end;
-
- procedure enter(var q:queue_type;var v:char);
- { this procedure adds an entry to the queue at the rear }
- begin
- if not full(q) then
- begin
- q.r:=(q.r+1) mod queue_size;
- q.data[q.r].c:=v;
- q.err:=false
- end
- else
- q.err:=true
- end;
-
- procedure clear(var q:queue_type);
- { this procedure clears a queue }
-
- begin
- q.r:=0;
- q.f:=0;
- q.err:=false;
- q.DepthFlag:=false;
- end;
-
- procedure SendFlow(com:comtype;C:Char);
- { this procedure will send an byte }
- var
- WaitCount : integer;
- begin
- WaitCount:=0;
- while OutputXoffed[com] and (WaitCount < MaxWait) do
- begin
- WaitCount:=WaitCount+1;
- Delay(100);
- end;
- If WaitCount >= MaxWait then
- OutputXoffed[com]:=false;
- WriteCom(com,C);
- end;
-
- procedure SendStr(com:comtype;S:Str255);
- var
- I : integer;
- begin
- for I:=1 to Length(S) do
- SendFlow(com,S[I]);
- SendFlow(com,#10);
- end;
-
- procedure leave(var q:queue_type;var v:char;com:comtype);
- { this procedure removes an entry from the queue at the front }
- begin
- if not empty(q) then
- begin
- If q.DepthFlag then
- If depth(q)<=max_queue_depth then
- WriteCom(com,CXon);
- q.f:=(q.f+1) mod queue_size;
- v:=q.data[q.f].c;
- q.err:=false
- end
- else
- q.err:=true
- end;
-
- {-----------------------------------------------------------------------------
- If the queue indexes are not equal then Receive returns the
- char from the COM1: port. The character is read from the queue and
- is stored in queue_value. If there is nothing in the queue after
- WaitTime loops then NoResponce is set to true and a null is returned.
- -----------------------------------------------------------------------------}
-
- procedure Receive( var C : char;
- var NoResponce : boolean;
- WaitTime : integer;
- var queue : queue_type;
- com : comtype );
- var
- CheckCount : integer;
-
- begin
- NoResponce:=false;
- If not empty(queue) then
- leave(queue,C,com)
- else
- begin { queue initially empty }
- CheckCount:=0;
- repeat { wait and see if anything comes in }
- Delay(100);
- CheckCount:=CheckCount+1;
- Until not empty(queue) or (CheckCount>WaitTime); { anything there }
- If not empty(queue) then
- leave(queue,C,com)
- else
- begin { Nothing came in }
- C:=#0;
- NoResponce:=true;
- end; { Nothing came in }
- end; { queue initially empty }
- end;