home *** CD-ROM | disk | FTP | other *** search
- PROGRAM Comm_TP4; {-This is the comment column }
- { which will be used to tell }
- { Written by: Kevin R. Bulgrien Version 1.01 completed 01/31/89 } { 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 }
- { and Comm_TC2 works with Turbo C. } { code documentation is far }
- { } { easier to read when it is }
- { Version History } { done like this... }
- { } { }
- { 1.00, 11/88 Original code uploaded to GEnie's Borland Roundtable } { }
- { 1.01, 02/89 Bug found & fixed in Procedure RemoveInt. Port [$21] was ANDed } { }
- { with $08 or $10 instead of ORed with those values. } { }
- { }
- USES DOS, CRT; { }
- { }
- CONST { }
- MaxSize = 511; {-Maximum input buffer size }
- { }
- 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 }
- { }
- PROCEDURE DisableInts; INLINE ($FA); {-Disable hardware interrupts}
- { }
- PROCEDURE EnableInts; INLINE ($FB); {-Enable hardware interrupts }
- { }
- { 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, StopBits, DataBits : 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 = (None,Odd,Null,Even,MarkOff,Mark,SpaceOff,Space); {-Parity settings supported }
- { }
- VAR {-Temporary variable to hold }
- Parameters : BYTE; { correct LCR register value }
- BEGIN { }
- IF (Com <= MaxPorts) {-Check validity of Com }
- THEN 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 { }
- ELSE BEGIN { }
- WRITELN (#13,#10, 'Error! COM', Com, ' not available', #10); {-Mostly here for debugging }
- END; { purposes }
- 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 }
- { [IntCom] + 1) MOD (MaxSize + 1) = InHead [IntCom]. The buffer is empty when InTail [IntCom] = InHead }
- { [IntCom]. InTail [IntCom] 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. NOTE: Stack checking must }
- { always be turned off in interrupt handlers ($S-). }
- { }
- {$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 {-Receive Register Full }
- 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 }
- InTail [IntCom] := (InTail [IntCom] + 1) MOD (MaxSize + 1); { the queue buffer pointer }
- END { }
- ELSE BEGIN {-If the buffer is full, the }
- IF (PORT [RS232 [IntCom].RHR] = $00) THEN { DO Nothing }; { data is read & not stored }
- END; { }
- 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 NOT IntInstalled [Com] AND (Com <= MaxPorts) {-Don't install the handler }
- THEN BEGIN { twice or if nonexistant. }
- 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 { to recieve data }
- ELSE BEGIN { }
- WRITE (#13, #10, 'Error! COM', Com, ' '); { }
- IF IntInstalled [Com] { }
- THEN WRITELN ('interrupt already installed',#10) {-Mostly here for debugging }
- ELSE WRITELN ('not available',#10) { purposes. Remove in your }
- END; { final program if you want. }
- 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. ORing $10 with PORT [$21] disables IRQ4 (COM1), while ORing $08 }
- { disables IRQ3 (COM2). Hardware interrupts must be disabled during this process. }
- { }
- PROCEDURE RemoveInt (Com : BYTE); { }
- BEGIN { }
- IF IntInstalled [Com] {-Don't remove if interrupt }
- THEN BEGIN { has not been installed }
- DisableInts; { }
- CASE Com OF { }
- 1 : PORT [$21] := PORT [$21] OR $10; {-Disable 8259 IRQ4 handling }
- 2 : PORT [$21] := PORT [$21] OR $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. Remove }
- SETINTVEC ($0D - Com, OldIntVector [Com]); { if modem shouldn't hang up }
- IntInstalled [Com] := FALSE; { when you use RemoveInt. }
- EnableInts; {-The original interrupt is }
- END { restored by SETINTVEC. }
- 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. }
- LoopVar := LoopVar + 1; { }
- WHILE (TimeLoop < 65535) AND ((PORT [RS232 [Com].LSR] AND $20) <> $20) DO {-Do not try to send data if }
- TimeLoop := TimeLoop + 1; { 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; { }
- { }
- { 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 }
- 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 { }
- 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 (None); { }
- ComSettings [1] . Stop := 1; { }
- ComSettings [1] . Bits := 8; { }
- ComSettings [2] . Baud := ORD (B2400); {-Define COM2 default setup }
- ComSettings [2] . Parity := ORD (None); { }
- 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! }