PROGRAM COMM; {This program allows one to use the IBM PC as a dumb terminal on ports COM1 or COM2. Code was taken from PC TECH JOURNAL Vol. 3, #12 Dec. 1985. pp. 181-187 Entered by Gregg Leichtman Date: 2/17/86 } TYPE INS8250 = RECORD {Define serial port registers} THR : INTEGER; {Transmit holding register} RBR : INTEGER; {Receive holding register} IER : INTEGER; {Interrupt enable register} LCR : INTEGER; {Line control register} MCR : INTEGER; {Modem control register} LSR : INTEGER; {Line status register} MSR : INTEGER; {Modem status register} END; {RECORD} REGISTERS = RECORD {Define the 8086 registers} AX,BX,CX,DX,BP,SI,DI,DS,ES,FLAGS : INTEGER; END; {RECORD} BAUD = (B110,B150,B300,B600,B1200,B2400,B4800,B9600); PARITY = (NONE,ODD,NEVERMIND,EVEN); {*** Define addresses for COM1 & COM2 ***} CONST RS232 : ARRAY[1..2] OF INS8250 = ((THR : $3F8; RBR : $3F8; IER : $3F9; LCR : $3FB; MCR : $3FC; LSR : $3FD; MSR : $3FE), (THR : $2F8; RBR : $2F8; IER : $2F9; LCR : $2FB; MCR : $2FC; LSR : $2FD; MSR : $2FE)); VAR COM : BYTE; {COM1 = 1 and COM2 = 2} RS_ERROR : BYTE; {Error from serial port} BUFFER : ARRAY[0..4095] OF BYTE; {4K data comm. buffer} BUF_HEAD, BUF_TAIL : INTEGER; {Head/tail buffer pointers} REGS : REGISTERS; {Passes parameters to MSDOS} CHAR_IN : INTEGER; {Character read from serial port} CONST DATA_SEGMENT : INTEGER = 0; {Holds contents of DS register} FUNCTION KEYIN : INTEGER; {This function reads in one keystroke from the keyboard. If a normal key was pressed, the value is returned in the low order byte of the integer value. If an "extended" key (such as a function key) was pressed, the low order byte contains a zero and the high order byte contains the extended code. } VAR KEY : BYTE; BEGIN REGS.AX := $0700; MSDOS(REGS); KEY := LO(REGS.AX); IF KEY = 0 THEN BEGIN KEYIN := KEYIN*256 END ELSE BEGIN KEYIN := KEY END END; {function KEYIN} PROCEDURE RS232_INTERRUPT; {This procedure handles interrupts from the serial port. RS_ERROR is set to reflect any error result from the port, but nothing is done with this. The character is read in from the port and stored in the buffer and BUF_TAIL is incremented. No test is made for buffer overflow. After all registers have been pushed, the data segment containing global data is loaded into DX from the typed constant DATA_SEGMENT. } BEGIN INLINE ($50/ {PUSH AX} $53/ {PUSH BX} $51/ {PUSH CX} $52/ {PUSH DX} $57/ {PUSH DI} $56/ {PUSH SI} $06/ {PUSH ES} $1E/ {PUSH DS} $FB/ {STI } $2E/$A1/DATA_SEGMENT/ {MOV AX, [CS:DATA_SEGMENT]} $8E/$D8); {MOV DS, AX} RS_ERROR := PORT[RS232[COM].LSR] AND $1E; BUFFER[BUF_TAIL] := PORT[RS232[COM].RBR]; BUF_TAIL := (BUF_TAIL+1) MOD 4096; INLINE ($FA); {CLI} PORT[$20] :=$20; {Clear interrupt flag.} INLINE ($1F/ {POP DS} $07/ {POP ES} $5E/ {POP SI} $5F/ {POP DI} $5A/ {POP DX} $59/ {POP CX} $5B/ {POP BX} $58/ {POP AX} $8B/$E5/ {MOV SP,BP} $5D/ {POP BP} $CF); {RETURN} {IRET} END; {of RS232_INTERRUPT} PROCEDURE RS232_INIT( SPEED : BAUD; P : PARITY; STOP, LENGTH : BYTE ); {This procedure uses Interrupt 14 to initialize the serial port.} BEGIN REGS.DX := COM - 1; REGS.AX := ORD(SPEED)*32 + ORD(P)*8 + (STOP-1)*4 + LENGTH-5; INTR($14,REGS) END; {procedure RS232_INIT} FUNCTION RS232_OUT( PARAM : BYTE ) : BOOLEAN; {This function outputs a byte to the serial port. In the event of a timeout, a value of TRUE is returned. A timeout occurs if we cannot get Data Set Ready, Clear to Send, and an empty transmit holding register after 65535 attempts. } VAR COUNTER : REAL; BEGIN COUNTER := 0; PORT[RS232[COM].MCR] := $0B; WHILE ((PORT[RS232[COM].MSR] AND $30) <> $30) {DSR & CTS} AND ((PORT[RS232[COM].LSR] AND $20) <> $20) {Tr. reg.} {empty} AND (COUNTER<65535.0) DO BEGIN COUNTER := COUNTER + 1 END; {WHILE} IF COUNTER = 65535.0 THEN BEGIN RS232_OUT := TRUE {We timed out ! } END ELSE BEGIN PORT[RS232[COM].THR] := PARAM; RS232_OUT := FALSE END END; {function RS232_OUT} PROCEDURE INITIALIZE; {This procedure does the following: 1) Changes the appropriate interrupt vector to point to RS232_INTERRUPT. 2) Enables the appropriate interrupt on the PC interrupt controller. 3) Enables the "data ready" interrupt on the serial board. } BEGIN REGS.AX := $250D-COM; REGS.DS := CSEG; REGS.DX := OFS(RS232_INTERRUPT); MSDOS(REGS); {**** Set up interrupt vector. ****} IF COM = 1 THEN BEGIN PORT[$21] := PORT[$21] AND $EF {Enable IRQ4} END ELSE BEGIN PORT[$21] := PORT[$21] AND $F7 {Enable IRQ3} END; PORT[RS232[COM].LCR] := PORT[RS232[COM].LCR] AND $7F; PORT[RS232[COM].IER] := 1; PORT[RS232[COM].MCR] := $08 END; {procedure INITIALIZE} PROCEDURE CLEANUP; {This procedure disables the "data ready" interrupt.} BEGIN PORT[$21] := PORT[$21] OR $18; PORT[RS232[COM].LCR] := PORT[RS232[COM].LCR] AND $7F; PORT[RS232[COM].IER] := 0; PORT[RS232[COM].MCR] := 0 END; {procedure CLEANUP} PROCEDURE TTY; {This procedure provides basic terminal emulation. Characters read from the keyboard are sent to the serial port. Characters read from the serial port are sent to the screen. The proced- ure is terminated by typing a CTRL-Z. } BEGIN CLRSCR; CHAR_IN := 0; REPEAT {This code does NOT suppres NULLS ! } WHILE BUF_TAIL <> BUF_HEAD DO BEGIN {getting data that is in the buffer.} WRITE(TRM,CHAR(BUFFER[BUF_HEAD])); BUF_HEAD := (BUF_HEAD + 1) MOD 4096 END; {WHILE} IF KEYPRESSED THEN BEGIN {processing a keystroke.} CHAR_IN := KEYIN; IF CHAR_IN <> 26 THEN {CTRL-Z to stop.} BEGIN IF CHAR_IN = 13 THEN BEGIN WRITE(TRM,CHAR(10)) END; IF RS232_OUT(LO(CHAR_IN)) THEN BEGIN WRITELN(TRM,'*** RS232 Timeout ***') END END END UNTIL CHAR_IN = 26 {CTRL-Z} END; {procedure TTY} BEGIN {-=* M A I N *=-} COM := 1; {Default to COM1} BUF_HEAD := 0; BUF_TAIL := 0; DATA_SEGMENT := DSEG; {Store the contents of the DS register.} { Initialize the port to 1200 baud, even parity, 2 stop bits,} { 7 data bits.} RS232_INIT(B4800,EVEN,1,7); INITIALIZE; TTY; CLEANUP END. {-=* M A I N *=-}