telegard/newcom.pas

551 lines
48 KiB
ObjectPascal

unit newcom;
{ which will be used to tell }
{ Written by: Kevin R. Bulgrien Version 1.00 completed 11/11/88 } { what the program is doing }
{ } { where its not self-evident }
{ Contact at: LeTourneau College LeTourneau College BBS } { }
{ Microcomputer Services 2400/1200/300 Baud } { Yes, it is wider than 80 }
{ P.O. Box 7001 (214) 237-2742 } { columns so you will need }
{ Longview, TX 75607 } { to use compressed print to }
{ } { print it out. }
{ This program works with Turbo Pascal 4.0 and 5.0. See Comm_TP4.DOC for the } { }
{ instructions. Comm_TP3, by the same author, works under Turbo Pascal 3.0. } { Don't complain too much as }
{ code documentation is far }
interface
USES DOS, CRT; { easier to read when it is }
{ done like this... }
CONST { }
(* MaxSize = 511; {-Maximum input buffer size }*)
maxsize = 5120;
{ }
TYPE {-8250 Communications Chip }
INS8250 = RECORD { -------------------------- }
THR : INTEGER; { Transmit Holding Register }
RHR : INTEGER; { Receive Holding Register }
DLL : INTEGER; { Divisor Latch Register LSB }
IER : INTEGER; { Interrupt Enable Register }
DLM : INTEGER; { Divisor Latch Register MSB }
IIR : INTEGER; { Interrupt ID Register }
LCR : INTEGER; { Line Control Register }
MCR : INTEGER; { Modem Control Register }
LSR : INTEGER; { Line Status Register }
MSR : INTEGER; { Modem Status Register }
END; { }
{ }
ComSettingsRecord = RECORD {-Used to hold the current }
Baud : BYTE; { settings of COM1 or COM2 }
Parity : BYTE; { }
Stop : BYTE; { }
Bits : BYTE; { }
END; { }
{ }
ComSettingsType = ARRAY [1..2] OF ComSettingsRecord; {-COM1 & COM2 Settings }
{ }
BaudType = (B110,B150,B300,B600,B1200,B2400,B4800,B9600,B19200,B38400); {-Baud rates supported }
{ }
ParityType = (None, Odd, Null, Even, MarkOff, Mark, SpaceOff, Space); {-Parity types supported }
{ }
ComBuffersType = ARRAY [1..2, 0..MaxSize] OF BYTE; {-The input buffers for COM1 }
{ and COM2 }
outbuffer=string[255];
const
RS232 : ARRAY [1..2] OF INS8250 = ( ( THR:$3F8; RHR:$3F8; DLL:$3F8; {-COM1 addresses of the 8250 }
IER:$3F9; DLM:$3F9; IIR:$3FA; { registers so that they may }
LCR:$3FB; MCR:$3FC; LSR:$3FD; { be accessed by name. }
MSR:$3FE ), { }
( THR:$2F8; RHR:$2F8; DLL:$2F8; {-COM2 addresses of the 8250 }
IER:$2F9; DLM:$2F9; IIR:$2FA; { registers so that they may }
LCR:$2FB; MCR:$2FC; LSR:$2FD; { be accessed by name }
MSR:$2FE ) ); { }
{ }
var { }
intinstalled:array[1..2] of boolean; {-TRUE if interrupt in place }
oldintvector:array[1..2] of pointer; {-Original COMx int. vectors }
inhead,intail:array[1..2] of word; {-Input buffer pointers }
carrier:array[1..2] of boolean; {-TRUE if Carrier Detected }
comsettings:comsettingstype; {-COM1 & COM2 line settings }
inbuffer:combufferstype; {-Input circular queue buffer}
exitsave:pointer; {-Saves original ExitProc }
maxports:word; {-Number of usable COM ports }
regs:registers; {-8088 CPU Registers }
currentcom:byte; {-COM port currently logged }
mpcoder:boolean;
mpcode:array[1..6] of byte;
{ }
procedure setuprs232(com,baud,parity,databits,stopbits:byte);
procedure installint(com:byte);
procedure removeint(com:byte);
procedure comsend1(com:byte; c:char);
procedure writecom(com:byte; data:outbuffer);
function readcom(com:byte):char;
procedure tty(localecho:boolean);
implementation
{rch11172000 not under Linux...}
(*
PROCEDURE DisableInts; INLINE ($FA); {-Disable hardware interrupts}
PROCEDURE EnableInts; INLINE ($FB); {-Enable hardware interrupts }
*)
PROCEDURE DisableInts;
begin
end;
PROCEDURE EnableInts;
begin
end;
{ }
{ This procedure sets up the selected COM port to the specified parameters. The Com parameter specifies the }
{ port to set up. It must be in the range 1 to 2, and is checked for errors. The Baud parameter is must be }
{ in the range 0 to 9, and is not range checked. The TYPE BaudTable is included only to document the baud }
{ rates supported in BaudTable. I.E. BaudTable [1] refers to 150 Baud. It is unneccessary for any other }
{ purpose. In the same manner, ParityType is provide to document the parity settings allowed. Use ORD() to }
{ get the correct BYTE value to pass: ORD(B110) returns the BYTE that selects 110 baud and ORD(None) gives }
{ the BYTE that selects no parity. (Use global declarations of these TYPEs for these examples to work.) 1.5 }
{ stop bits are used when StopBits = 2 AND DataBits = 5, but otherwise StopBits will set the correct number }
{ of stop bits in the range 1 to 2. DataBits may be set with 5 to 8 for the number of data bits to use. }
{ }
{ Mark parity means that parity is enabled and the parity bit is always set to 0. Space parity means that }
{ parity is enabled and the parrity bit is always set to 1. MarkOff and SpaceOff indicate that Mark or Space}
{ parity is chosen but parity is disabled. Functionally they are equivalent to NONE - as is NULL. }
{ }
procedure setuprs232(com,baud,parity,databits,stopbits:byte); { }
const {-These values set the baud }
baudtable:array[0..9] of word=($0417,$0300,$0180,$00C0,$0060, { rate of the 8250 when they }
$0030,$0018,$000C,$0006,$0003); { are written to DLL & DMM. }
type { }
baudtype=(b110,b150,b300,b600,b1200,b2400,b4800,b9600,b19200,b38400); {-Baud rates supported }
paritytype=(pnone,podd,pnull,peven,pmarkoff,pmark,pspaceoff,pspace); {-Parity settings supported }
var parameters:byte; {-Temporary variable to hold }
{ correct LCR register value }
begin
disableInts; {-Always when writing to 8250}
port[rs232[com].MCR]:=$00; {-DTR & RTS off while setting}
port[rs232[com].LCR]:=port[rs232[com].LCR] or $80; {-Allow access to DLL & DLM }
port[rs232[com].DLL]:=lo(baudtable[baud]); {-Set the baud rate }
port[rs232[com].DLM]:=hi(baudtable[baud]); { }
parameters:=(databits-5) and $03; {-Build the value to write }
parameters:=parameters or (((stopbits-1) shl 2) and $04); { to Line Control Register. }
parameters:=parameters or ((parity shl 3) and $38); { }
port[rs232[com].LCR]:=parameters; {-Set parity, data/stop bits }
port[rs232[com].MCR]:=$0B; {-DTR & RTS back on }
EnableInts; {-Done writing to 8250 regs. }
end; { }
{ }
{ This procedure handles all interrupts from the 8250 communications chip. All interrupt types are provided }
{ for even though I only implemented the receive data interrupt and crudely used the modem status interrupt. }
{ The skeleton is there though, so you can write your own implementations for the interrupts. Incoming data }
{ is stored in InBuffer if the buffer is not full - otherwise it is ignored. The buffer is full when (InTail }
{ + 1) MOD (MaxSize + 1) = InHead. The buffer is empty when InTail = InHead. InTail is incremented so that }
{ it always points to where the next item will be put. Modem (port) status is monitored for Carrier Detect. }
{ The global BOOLEAN variable 'Carrier' always shows the status of each COM port if its interrupt handler is }
{ active. TRUE = Carrier Detected and FALSE = No Carrier Detected. This is done for programs that will use }
{ a modem for input. }
{ }
function iii(intcom:byte; i:integer):byte;
var j:integer;
begin
j:=intail[intcom]-i;
if (j<0) then inc(j,maxsize+1);
iii:=inbuffer[intcom,j];
end;
procedure checkitout(intcom:integer);
var i:integer;
begin
if ((iii(intcom,1)=254) and (iii(intcom,2)=253)) then
if ((iii(intcom,9)=1) and (iii(intcom,10)=2) and
(iii(intcom,11)=1)) then begin
mpcoder:=TRUE;
for i:=1 to 6 do mpcode[7-i]:=iii(intcom,i+2);
inhead[intcom]:=intail[intcom];
end;
end;
{$F+} {-Interrupt handlers MUST be }
procedure inthandler; interrupt; { FAR calls. }
{$F-} { }
var {-The COMx port which is to }
intcom:byte; { be used. }
begin { }
PORT [$20] := $0B; {-Allow access to 8259 ISR }
intcom:=3-((port[$20] and $18) shr 3); {-Detect interrupting port }
CASE PORT [RS232 [IntCom].IIR] AND $06 OF { }
0 : begin {-Modem Status Change Int. }
carrier[intcom]:=($80 and port[rs232[intcom].MSR]=$80); {-Save status of Carrier }
end;
2 : begin {-Transmit Register Empty }
end;
4 : begin
disableints;
port[rs232[intcom].LCR]:=port[rs232[intcom].LCR] and $7F; {-Allow THR,RBR & IER access }
if (intail[intcom]+1) mod (maxsize+1)<>inhead[intcom] then begin {-If the buffer is not full, }
inbuffer[intcom,intail[intcom]]:=port[rs232[intcom].RHR]; { add the character and set }
(* if ((iii(intcom,0)=255) and (not mpcoder)) then checkitout(intcom);*)
intail[intcom]:=(intail[intcom]+1) mod (maxsize+1); { the queue buffer pointer }
end else {-If the buffer is full, the }
if (port[rs232[intcom].RHR]=$00) then { DO Nothing }; { data is read & not stored }
enableints;
end;
6 : begin {-Line Status change & Error }
end;
END;
PORT [$20] := $20; {-Notify 8259 that interrupt }
END; { has been completed. }
{ This procedure installs and enables the specified serial port interrupt. It also forces the appropriate }
{ input buffer to the empty state. Carrier is initialized to the current state of the Carrier Detect line }
{ on the port. The old serial port interrupt vector is saved so that it can be reinstalled when we remove }
{ our serial port interrupt. DTR and RTS are forced to the ready state and the 8250 interrupts are enabled }
{ by writing $0B to the MCR. To enable all four types of 8250 interrupts, write $0F to the IER. (I enabled }
{ the Modem Status Change and Receive Buffer Full interrupts by writing $09 to the IER). ORing $EF with PORT }
{ [$21] enables IRQ4 (COM1), while ORing $F7 enables IRQ3 (COM2). Hardware interrupts should be disabled }
{ during the installation process since the 8250 & 8259 ports are being accessed. }
procedure installint(com:byte);
begin
if (intinstalled[com]) then removeint(com);
disableints;
InTail [com]:=0; {-Set input buffer to empty }
InHead [com]:=0;
Carrier [Com] := ($80 AND PORT [RS232 [Com].MSR] = $80); {-Read Carrier Detect status }
PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F; {-Allow THR,RBR & IER access }
PORT [RS232 [Com].IER] := $00; {-Disable 8250 interrupts }
IF PORT [RS232 [Com].LSR] <> 0 THEN { Nothing }; {-Reset interrupts that were }
IF PORT [RS232 [Com].RHR] <> 0 THEN { Nothing }; { waiting to be processed }
GETINTVEC ($0D - Com, OldIntVector [Com]); {-Save old interrupt vector }
SETINTVEC ($0D - Com, @IntHandler); {-Load new interrupt vector }
IntInstalled [Com] := TRUE; {-The interrupt is installed }
CASE Com OF
1 : PORT [$21] := PORT [$21] AND $EF; {-Enable 8259 IRQ4 handling }
2 : PORT [$21] := PORT [$21] AND $F7; {-Enable 8259 IRQ3 handling }
END;
PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F; {-Allow THR,RBR & IER access }
PORT [RS232 [Com].IER] := $01; {-Enable 8250 interrupts }
PORT [RS232 [Com].MCR] := $0B; {-Set DTR & RTS so the other }
EnableInts; { device knows we are ready }
END;
{ This procedure removes the specified serial port interrupt and reinstalls the original interrupt vectors. }
{ DTR & RTS are set OFF and 8250 interrupts are disabled by writing $00 to the MCR. All 8250 interrupt types }
{ are disabled by writing $00 to the IER. ANDing $10 with PORT [$21] disables IRQ4 (COM1), while ANDing $08 }
{ disables IRQ3 (COM2). Hardware interrupts must be disabled during this process. }
PROCEDURE RemoveInt (Com : BYTE);
BEGIN
DisableInts;
CASE Com OF
1 : PORT [$21] := PORT [$21] AND $10; {-Disable 8259 IRQ4 handling }
2 : PORT [$21] := PORT [$21] AND $08; {-Disable 8259 IRQ3 handling }
END;
PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F; {-Allow THR,RBR & IER access }
PORT [RS232 [Com].IER] := $00; {-Disable 8250 interrupts }
PORT [RS232 [Com].MCR] := $00; {-Set DTR & RTS off }
SETINTVEC ($0D - Com, OldIntVector [Com]); {-Load old interrupt vector }
IntInstalled [Com] := FALSE; {-The interrupt is removed }
EnableInts;
(* END
ELSE BEGIN {-Mostly here for debugging }
WRITE (#13, #10, 'Error! COM', Com, ' '); { purposes. Remove in your }
WRITELN ('interrupt is not installed', #10); { program if you wish. }
END;*)
END;
{ This procedure writes character or string data to the serial port. It does this by directly reading and }
{ writing to the 8250 communications chip. This is an example that may be modified to suit your purposes. }
{ As is, it pauses the program while it sends the data. If it cannot send a character after 65535 tries, it }
{ aborts the sending process. This could easily be converted to a FUNCTION that returns the BOOLEAN value }
{ TimeOut. The statement: (PORT [RS232 [Com].LSR] AND $20) <> $20 indicates when the THR is ready for a new }
{ character to send. CTS and DSR are not checked, but if you want to check for them (PORT [RS232 [Com].MSR] }
{ AND $30) must equal $30. Interrupts must be disabled while using the 8250 port registers. }
procedure writecom(com:byte; data:outbuffer);
var
LoopVar, {-Pointer to output char }
TimeLoop : WORD; {-Timeout counter variable }
TimeOut : BOOLEAN; {-True if unable to send }
BEGIN
LoopVar := 0;
TimeOut := FALSE;
WHILE (LoopVar < LENGTH (Data)) AND NOT TimeOut DO {-Send the data one char at }
BEGIN { a time unless the port was }
TimeLoop := 0; { timed out. }
inc(loopvar);
WHILE (TimeLoop < 65535) AND ((PORT [RS232 [Com].LSR] AND $20) <> $20) DO {-Do not try to send data if }
inc(timeloop); { the THR is not empty yet. }
IF TimeLoop <> 65535
THEN BEGIN
DisableInts;
PORT [RS232 [Com].LCR] := PORT [RS232 [Com].LCR] AND $7F; {-Allow THR,RBR & IER access }
PORT [RS232 [Com].THR] := ORD (Data [LoopVar]); {-Put the data to send in }
EnableInts; { the THR }
END
ELSE BEGIN
TimeOut := TRUE; {-WriteCOM aborts if the THR }
WRITELN (#13,#10, 'Timeout on COM', Com); { takes too long to become }
END; { empty so you can send more }
END; { data }
END;
procedure comsend1(com:byte; c:char);
var timeloop:word;
begin
timeloop:=0;
while ((timeloop<65535) and ((port[rs232[com].LSR] and $20)<>$20)) do
inc(timeloop);
if (timeloop<>65535) then begin
{ disableints;}
{ port[rs232[com].LCR]:=port[rs232[com].LCR] and $7F;}
port[rs232[com].THR]:=ord(c);
{ enableints;}
end; {else
writeln(^M^J+'Timeout on COM',com);}
end;
{ This function is an example of how to get a character from the serial port. As is, if the buffer is empty, }
{ it waits until a character arrives, so this will not work for the TTY emulation. The interrupts are always }
{ disabled when the buffer pointers are checked or modified. Beware! Do not completely disable interrupts }
{ in the wait loop or else you never will get a character if there is not one there already. }
{ }
FUNCTION ReadCOM (Com : BYTE) : CHAR; { }
VAR { }
CharReady : BOOLEAN; {-TRUE if there is data in }
BEGIN { the input buffer }
CharReady := FALSE; { }
REPEAT {-Wait for data to arrive }
DisableInts; { }
CharReady := InTail [Com] <> InHead [Com]; {-Check to see if buffer is }
EnableInts; { empty }
UNTIL CharReady; { }
DisableInts; { }
ReadCOM := CHR(InBuffer [Com, InHead [Com]]); {-Read a character of data }
InHead [Com] := (InHead [Com] + 1) MOD (MaxSize + 1); {-Update the buffer pointer }
EnableInts; { }
END; { }
{ }
{ End of RS-232 handler routines ----------- Start of TTY emulation routines } { Example TTY program starts }
{ }
{ A crude but effective procedure to allow the user to change settings of a COM port in use. CurrentCom and }
{ ComSettings determine how the port is currently set up. As the parameters are changed, ComSettings is also }
{ updated. Once again, keep in mind that the object of this program is not to provide a glamorous terminal }
{ program. Rather it serves as a simple model for those wanting to incorporate serial routines in their own }
{ programs. }
{ }
PROCEDURE SetUpPort (Com : BYTE); { }
VAR { }
ResetPort : BOOLEAN; {-TRUE when settings changed }
InkeyChr : CHAR; {-Keyboard input variable }
BEGIN { }
WRITELN; { }
ResetPort := FALSE; { }
WRITELN ('COM', Com, ' Setup', #10); {-Select a baud rate }
WRITELN ('0) 110 5) 2400'); { }
WRITELN ('1) 150 6) 4800'); {-Note that defaults are }
WRITELN ('2) 300 7) 9600'); { allowed if you press <CR> }
WRITELN ('3) 600 8) 19200'); { at any of the prompts. The }
WRITELN ('4) 1200 9) 38400', #10); { port is not reset unless }
WRITE ('Select a baud rate [', ComSettings [Com] . Baud, ']: '); { the defaults are changed. }
REPEAT { }
InkeyChr := READKEY; { }
UNTIL (InkeyChr IN ['0'..'9', #13]); { }
WRITELN (InkeyChr, #10); { }
IF (InkeyChr <> #13) { }
THEN BEGIN { }
ComSettings [Com] . Baud := ORD (InkeyChr) - 48; { }
ResetPort := TRUE; { }
END; { }
WRITELN ('0) None 2) None'); {-Select a parity setting }
WRITELN ('1) Odd 3) Even', #10); { }
WRITE ('Select a parity type [', ComSettings [Com] . Parity, ']: '); { }
REPEAT { }
InkeyChr := READKEY; { }
UNTIL (InkeyChr IN ['0'..'3', #13]); { }
WRITELN (InkeyChr, #10); { }
IF (InkeyChr <> #13) { }
THEN BEGIN { }
ComSettings [Com] . Parity := ORD(InkeyChr) - 48; { }
ResetPort := TRUE; { }
END; { }
WRITE ('Select number of stop bits [', ComSettings [Com] . Stop, ']: '); {-Select number of stop bits }
REPEAT { }
InkeyChr := READKEY; { }
UNTIL (InkeyChr IN ['1'..'2', #13]); { }
WRITELN (InkeyChr, #10); { }
IF (InkeyChr <> #13) { }
THEN BEGIN { }
ComSettings [Com] . Stop := ORD(InkeyChr) - 48; { }
ResetPort := TRUE; { }
END; { }
WRITE ('Select number of data bits [', ComSettings [Com] . Bits, ']: '); {-Select number of data bits }
REPEAT { }
InkeyChr := READKEY; { }
UNTIL (InkeyChr IN ['5'..'8', #13]); { }
WRITELN (InkeyChr, #10); { }
IF (InkeyChr <> #13) { }
THEN BEGIN { }
ComSettings [Com] . Bits := ORD(InkeyChr) - 48; { }
ResetPort := TRUE; { }
END; { }
IF ResetPort {-If the settings changed, }
THEN SetupRS232 (Com, ComSettings [Com] . Baud, { reset the port }
ComSettings [Com] . Parity, { }
ComSettings [Com] . Stop, { }
ComSettings [Com] . Bits); { }
END; { }
{ }
{ This provides a simple terminal emulation that might be used to prove that these routines really work, and }
{ that they are not hard to use. I got to playing, and perhaps it got a bit more complex than necessary... }
{ but then again, who said it had to be quick and dirty. The LocalEcho parameter determines if characters }
{ typed on the keyboard should be echoed to the screen. }
{ }
PROCEDURE TTY (LocalEcho : BOOLEAN); { }
VAR { }
ExitTTY, {-TRUE when ready to quit }
DataReady : BOOLEAN; {-TRUE if buffer not empty }
OldCarrier : ARRAY [1..2] OF BOOLEAN; {-Helps detect carrier change}
Buffer : CHAR; {-A character buffer }
i:integer;
BEGIN { }
OldCarrier [1] := NOT Carrier [1]; {-Make Carrier Detect Status }
OldCarrier [2] := NOT Carrier [2]; { so it will be displayed }
DataReady := FALSE; {-Initialize everything }
ExitTTY := FALSE; { }
Buffer := #0; { }
CLRSCR; { }
WRITELN ('Terminal emulator commands', #10); {-Brief summary of command }
WRITELN ('<ALT C> Toggle Port in use COM1/COM2'); { keys that can be used }
WRITELN ('<Alt E> Toggle Local Echo On/Off'); { }
WRITELN ('<Alt P> Change Port Parameters'); { }
WRITELN ('<Alt X> Exit'); { }
REPEAT {-Terminal emulation starts }
DisableInts; { }
DataReady := (InTail [CurrentCom] <> InHead [CurrentCom]); {-If data has been received, }
EnableInts; { print one character }
IF DataReady { }
THEN BEGIN { CHR(12) is interpreted as }
DisableInts; { a FormFeed, and so clears }
Buffer := CHR(InBuffer [CurrentCom, InHead [CurrentCom]]); { the screen }
InHead [CurrentCom] := (InHead [CurrentCom] + 1) MOD (MaxSize + 1);{ }
EnableInts; { Input buffer is updated }
CASE Buffer OF { }
#12 : CLRSCR; { }
ELSE WRITE (Buffer); { }
END; { }
END; { }
IF (OldCarrier [CurrentCom] <> Carrier [CurrentCom]) {-If a change in carrier }
THEN BEGIN { detect occurs, notify the }
WRITELN; { user of the new status }
IF Carrier [CurrentCom] { }
THEN WRITELN ('CARRIER DETECTED (COM', CurrentCom, ')') { }
ELSE WRITELN ('NO CARRIER (COM', CurrentCom, ')'); { }
OldCarrier [CurrentCom] := Carrier [CurrentCom]; { }
END; {-If a key has been pressed, }
IF KEYPRESSED { process it }
THEN BEGIN { }
Buffer := READKEY; { }
IF (Buffer = #00) AND KEYPRESSED {-Extended key codes require }
THEN BEGIN { another read }
Buffer := READKEY; { }
CASE Buffer OF { }
#46 : IF (1 + ORD (CurrentCom = 1)) <= MaxPorts {-<ALT C> lets you toggle }
THEN BEGIN { between ports if the new }
CurrentCom := 1 + ORD (CurrentCom = 1); { port exists }
WRITELN (#13,#10, 'COM', CurrentCom); { }
END { }
ELSE BEGIN { }
WRITE (#13,#10, 'COM'); { }
WRITE (1 + ORD (CurrentCom = 1)); { }
WRITELN (' not available'); { }
END; { }
#18 : LocalEcho := NOT LocalEcho; {-<ALT E> toggles Local Echo }
#25 : SetupPort (CurrentCom); {-<ALT P> allows port setup }
#45 : ExitTTY := TRUE; {-<ALT X> exits the program }
ELSE WriteCOM (CurrentCom, CHR(27) + Buffer); {-Other extended key codes }
END; { are sent to the port }
END { }
ELSE BEGIN {-Normal key codes are sent }
CASE Buffer OF { or translated and sent }
#12 : BEGIN { }
WriteCOM (CurrentCom, Buffer); {-FormFeed clears screen if }
IF LocalEcho THEN CLRSCR; { local echo is on }
END; { }
#13 : BEGIN {-A carriage return also }
WriteCOM (CurrentCom, Buffer + CHR(10)); { sends a line feed }
IF LocalEcho THEN WRITELN; { }
END; { }
ELSE BEGIN {-All other characters are }
WriteCOM (CurrentCom, Buffer); { sent as typed }
IF LocalEcho THEN WRITE (Buffer); { }
END; { }
END; { }
END; { }
END; { }
UNTIL ExitTTY; {-Continue emulation until }
END; { <ALT X> is pressed. }
{ }
FUNCTION Equipment : WORD; {-This function returns what }
BEGIN { equipment is present on }
INTR ($11, Regs); { the machine it is running }
Equipment := Regs.AX; { on. }
END; { }
{ }
{$F+} {-VERY IMPORTANT! When the }
PROCEDURE RemoveIntOnExit; { program quits normally or }
{$F-} { abnormally, the interrupt }
BEGIN { handlers are uninstalled }
IF IntInstalled [1] { if they are still set up. }
THEN RemoveInt (1); { }
IF IntInstalled [2] { }
THEN RemoveInt (2); { }
ExitProc := ExitSave; {-Return control to the }
END; { original exit procedure }
{ }
begin
mpcoder:=FALSE;
(*
ExitSave := ExitProc; {-VERY IMPORTANT! This lets }
ExitProc := @RemoveIntOnExit; { the program halt safely. }
MaxPorts := (Equipment AND $0E00) SHR 9; {-Find # of system COM ports }
IntInstalled [1] := FALSE; {-No interrupt handlers are }
IntInstalled [2] := FALSE; { installed on start up }
ComSettings [1] . Baud := ORD (B9600); {-Define COM1 default setup }
ComSettings [1] . Parity := ORD (pNone); { }
ComSettings [1] . Stop := 1; { }
ComSettings [1] . Bits := 8; { }
ComSettings [2] . Baud := ORD (B2400); {-Define COM2 default setup }
ComSettings [2] . Parity := ORD (pNone); { }
ComSettings [2] . Stop := 1; { }
ComSettings [2] . Bits := 8; { }
IF (MaxPorts >= 1) { }
THEN BEGIN { }
SetupRS232 (1, ComSettings [1] . Baud, {-Initialize COM1 to the }
ComSettings [1] . Parity, { default setup }
ComSettings [1] . Stop, { }
ComSettings [1] . Bits); { }
InstallInt (1); {-Set up the COM1 interrupt }
END { if the computer has a port }
ELSE WRITELN ('Error! No serial ports installed in this computer'); { }
IF (MaxPorts >= 2) { }
THEN BEGIN { }
SetupRS232 (2, ComSettings [2] . Baud, { }
ComSettings [2] . Parity, {-Initialize COM2 to the }
ComSettings [2] . Stop, { default setup }
ComSettings [2] . Bits); { }
InstallInt (2); {-Set up the COM2 interrupt }
END; { }
CurrentCom := 1; {-Set COM1 as logged port }
TTY (FALSE); {-TTY with local echo off }
{ }
{ IMPORTANT: RemoveIntOnExit is always called when the program terminates! } {-RemoveIntOnExit invoked by }
{ Turbo. Don't quit without }
*)
END. { removing interrupts! }