home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
RBBS in a Box Volume 1 #2
/
RBBS_vol1_no2.iso
/
001g
/
dumtrm.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1985-03-19
|
19KB
|
431 lines
TITLE : Interrupt handler
PRODUCT : Turbo Pascal
VERSION : All
OS : Ms DOS 1.00 or greater
DATE : 2/1/85
{----------------------------------------------------------------------------
DumbTerm is an example 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". This program was
Written by,
Jim McCarthy
Technical Support
Borland International
and
Andy Batony
Teleware Incorporated
-----------------------------------------------------------------------------}
PROGRAM dumbterm;
CONST
hex : string[16] = '0123456789ABCDEF'; { Constant used to convert }
{ decimal to hex }
irq4 = $30; { Interrupt vector address for }
{ COM1. }
irq3 = $2C; { Vector for COM2. }
eoi = $20; { }
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 }
buffsize = 1024; { Size of the ring buffer }
TYPE { Type declarations }
str4 = string[4];
str80 = string[80];
ratetype = (rate300,rate1200,rate4800,rate9600);
comtype = (com1,com2);
bytechar = record case boolean of
true :(o:byte);
false:(c:char)
end;
regrec = record
ax,bx,cx,dx,bp,di,si,ds,es,flags : integer;
end;
VAR
segment : integer absolute cseg:$00A0; { Address for storing DS }
intbuffer : array [0..buffsize] of bytechar; { Ring buffer }
oldvecseg, { Segment of DOS set }
oldvecoff, { Offset of DOS set com int. }
head, { Index to the head of the }
{ ring buffer. }
tail, { Tail index of the ring buff }
comport, { Comport address }
i : integer; { Counter }
comp : comtype; { Used to specify which comport}
ch, { Temperary character buffer }
kch : char; { Char keyed in from the key- }
{ board }
temp : string[80]; { Temperary buffer }
tbyte,
lbyte : byte;
showok : boolean;
registers : regrec; { Registers used in DOS call }
{----------------------------------------------------------------------------
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 IntHandler;
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 }
tbyte := port[ comport ]; { Get the character in the port}
lbyte := port[ comport + linestat ]; { Get the status of the port }
If ( head < buffsize ) then { Check bounds of the ring }
head := head + 1 { buffer, and if smaller then }
else { increment by one otherwise }
head := 0; { set to the first element }
intbuffer[ head ].o := tbyte; { Load the buffer w/ the char. }
port[$20] := $20; { }
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;
{-----------------------------------------------------------------------------
The procedure AskCom gets the comport to comunicate through.
-----------------------------------------------------------------------------}
PROCEDURE AskCom( var comp : comtype );
VAR
ch : char;
BEGIN
write( 'What port is the modem in ( 1 or 2 ) : ' ); { Write prompt }
Repeat
read( kbd,ch ); { Get the character and }
Until ( ch in ['1','2'] ); { check bounds }
If ( ch = '1' ) then
Begin
writeln( 'COM1:' ); { Set to COM1 }
comp := com1;
End
else
Begin
writeln( 'COM2:' );
comp := com2; { Set to COM2 }
End;
END;
{-----------------------------------------------------------------------------
This procedure sets the baud rate of the comport to either 300, 1200,
4800, or 9600 baud. The Divisor latches are set according to the table in
the IBM hardware technical reference manual ( p. 1-238 ).
-----------------------------------------------------------------------------}
PROCEDURE SetRate(r:ratetype);
VAR
tlcr, { Line control register }
tdlmsb, { Divisor latch MSB }
tdllsb : byte; { Divisor latch LSB }
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+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;
{----------------------------------------------------------------------------
WhatRate is the input procedure that uses SetRate to set the correct
baud rate.
----------------------------------------------------------------------------}
PROCEDURE WhatRate;
BEGIN
writeln; { Display prompt }
write('what baud rate ([3]00,[1]200,[4]800,[9]600) ');
read(kbd,kch); { Read in the baud rate }
case kch of
'3':SetRate(rate300); { Set the corrisponding rate }
'1':SetRate(rate1200); { . }
'4':SetRate(rate4800); { . }
'9':SetRate(rate9600); { . }
end;
writeln(kch);
END;
{-------------------------------------------------------------------------
The procedure IntOn sets up the interrupt handler vectors, and
communication protocal.
-------------------------------------------------------------------------}
PROCEDURE IntOn(com:comtype);
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. }
parity=8;
evenparity=16;
dtrtrue=1;
rtstrue=2;
bit3true=8;
VAR
tbyte : byte; { Temperary byte buffer }
i : integer; { counter }
BEGIN
head:=0; { Initialize the ring buffer }
tail:=0; { indexes }
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 }
WhatRate; { Get the baud rate }
port[ comport + linectrl ] := bits7 + stopbit1 + noparity;
{ Set the protocall }
port[ comport + modemctrl ] := dtrtrue + rtstrue + bit3true;
port[ comport + intenreg ] := 1; { 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 }
dx:=ofs(IntHandler); { offset of the handler }
end;
case com of
com1: begin
oldvecoff:=memw[0000:irq4]; { Save the segment and offset }
oldvecseg:=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
oldvecoff:=memw[0000:irq3]; { Same as above }
oldvecseg:=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;
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;
VAR
tbyte:byte;
BEGIN
inline($FA); { CLI } { Disable interrupts }
tbyte:=port[$21]; { }
port[comport+intenreg]:=0; { Disable COM interrupts }
If comport=$3f8 then { If using COM1: then }
begin
port[$21]:=tbyte or $10; { }
memw[0000:irq4]:=oldvecoff; { Restore the DOS interrupt }
memw[0000:irq4+2]:=oldvecseg; { handler }
end
else
begin
memw[0000:irq3]:=oldvecoff; { Restore the DOS interrupt }
memw[0000:irq3+2]:=oldvecseg; { handler }
port[$21]:=tbyte or $08; { }
end;
END;
{-----------------------------------------------------------------------------
If the ring buffer indexes are not equal then ReadCom returns the
char from either the COM1: or COM2: port. The character is read from the
ring buffer and is stored in the FUNCTION result.
-----------------------------------------------------------------------------}
FUNCTION ReadCom : char;
BEGIN
If ( head <> tail ) then { Check for ring buffer character }
begin
If ( tail < buffsize ) then { Check the limits of the ring }
tail := tail + 1 { and set tail accordingly }
else
tail := 0;
ReadCom := intbuffer[tail].c; { Get the character }
end;
END;
{----------------------------------------------------------------------------
This procedure outputs directly to the communications port the byte
equivilent of the character to be sent.
----------------------------------------------------------------------------}
PROCEDURE WriteCom( ch : char );
VAR
tbyte:byte;
BEGIN
tbyte:=ord(ch); { Change to byte format }
port[comport]:=tbyte; { Output the character }
END;
{---------------------------------------------------------------------------
When the interrupt routine is called because of a com port interrupt
the head index is incremented by one, but does not increment the tail
index. This causes the two indexes to be unequal, and ModemInput to
become true.
---------------------------------------------------------------------------}
FUNCTION ModemInput:boolean;
begin
ModemInput:=(head<>tail);
end;
BEGIN
segment := dseg; { segment is an absolute variable used }
{ by the interrupt routine to restore }
{ the DS register to point to the DSEG }
AskCom( comp ); { Get the com port to use }
IntOn( comp ); { Set up the interrupt routine }
ch:=' '; { Initialize ch for the loop }
Repeat
If keypressed then { If a key is pressed on the keyboard }
begin
read(kbd,kch); { then the program reads it in and }
kch := Upcase(kch);
write( kch );
If ( kch = chr(13)) then writeln;
{ checks if the program should be ended }
WriteCom(kch); { Write the character to the com port }
end;
If ModemInput then { If something was placed in the ring }
begin { buffer then }
write('[');
ch:=ReadCom; { it is read in and printed to the }
write(ch); { screen }
If ch=chr(13) then writeln;
write(']');
end;
Until kch=chr(27); { This loops ends when <esc> is hit }
IntOff; { Restore the enviornment }
END. { Main program }