home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
d
/
dec92.zip
/
1012024A
< prev
next >
Wrap
Text File
|
1992-10-13
|
10KB
|
312 lines
/*
* TERM286.C Copyright (C) 1992 Mark R. Nelson.
*
* This file, along with TERM286.H and REAL_ISR.C, make up a simple
* terminal emulator that will run under the Phar Lap 286|DOS Extender.
* This program demonstrates the use of a bimodal interrupt handler.
* The protected mode interrupt is in this routine, the real mode
* interrupt is found in REAL_ISR.C. The real mode interrupt is
* linked into a DLL which is loaded at run time.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <dos.h>
#include "phapi.h"
#include "term286.h"
/*
* The inline version of outp() for Turbo C will generate a warning
* message under some circumstances. This turns off that message
* by removing the macro the defines the inline version.
*/
#ifdef __TURBOC__
#undef outp
#endif
/*
* These constants define the registers and bits used in setting
* up the 8250 UART.
*/
#define DIVISOR_LATCH_LSB 0
#define DIVISOR_LATCH_MSB 1
#define INTERRUPT_ENABLE_REGISTER 1
#define IER_RECEIVE_DATA_INTERRUPT 0x01
#define LINE_CONTROL_REGISTER 3
#define LCR_NUMBER_OF_STOP_BITS 0x04
#define LCR_PARITY_N 0x00
#define LCR_PARITY_O 0x08
#define LCR_PARITY_E 0x18
#define LCR_DIVISOR_LATCH_ACCESS 0x80
#define MODEM_CONTROL_REGISTER 4
#define MCR_DATA_TERMINAL_READY 1
#define MCR_REQUEST_TO_SEND 2
#define MCR_OUT2 8
#define LINE_STATUS_REGISTER 5
#define LSR_THRE 0x20
/*
* The real mode ISR and the Port data structure are both found in the
* real mode DLL. Because of this, they both have to be declared as
* being far data.
*/
extern struct port_data far Port;
void far interrupt real_isr( void );
/*
* This is the protected mode ISR. It is identical to the real mode
* ISR, except for the increment of the protected mode interrupt count
* near the end. This routine reads in the character that caused the
* interrupt, then tries to stuff it in the buffer and update the
* head pointer. Finally, it increments the diagnostic counter,
* outputs an EOI to the 8259 PIC, and exits.
*/
void far interrupt prot_isr()
{
unsigned char c;
int space_used;
c = ( unsigned char ) inp( Port.uart_address );
space_used = Port.head_pointer - Port.tail_pointer;
if ( space_used < 0 )
space_used += 1024;
if ( space_used < 1023 ) {
Port.buffer[ Port.head_pointer++ ] = c;
Port.head_pointer &= 1023;
}
Port.prot_count++;
outp( 0x20, 0x20 );
}
/*
* This routine sets up the 4 data format parameters for and 8250
* family UART. It also does a small amount of error checking, but
* doesn't affect interrupts. It can be used before or after the
* port has been opened.
*/
int SetComParameters( int baud_rate,
char parity,
int word_length,
int stop_bits )
{
int divisor;
int lcr_out;
if ( word_length < 5 || word_length > 8 )
return( 0 );
lcr_out = word_length - 5;
switch ( toupper( parity ) ) {
case 'N' :
break;
case 'O' :
lcr_out |= LCR_PARITY_O;
break;
case 'E' :
lcr_out |= LCR_PARITY_E;
break;
default :
return( 0 );
}
switch ( stop_bits ) {
case 1 :
break;
case 2 :
lcr_out |= LCR_NUMBER_OF_STOP_BITS;
break;
default :
return( 0 );
}
if ( baud_rate < 10 )
return( 0 );
divisor = ( int ) ( 115200L / baud_rate ) ;
outp( Port.uart_address + LINE_CONTROL_REGISTER,
LCR_DIVISOR_LATCH_ACCESS );
outp( Port.uart_address + DIVISOR_LATCH_LSB, divisor & 0xff );
outp( Port.uart_address + DIVISOR_LATCH_MSB, divisor >> 8 );
outp( Port.uart_address + LINE_CONTROL_REGISTER, lcr_out );
return( 1 );
}
/*
* This routine is the one that actually sets up the interrupt
* routines. It does this via several calls to Phar Lap 286
* Extender routines.
*/
void SetInterruptVectors( void )
{
REALPTR real_isr_ptr;
Port.irq_mask = 1 << ( Port.interrupt_number % 8 );
outp( Port.uart_address + INTERRUPT_ENABLE_REGISTER, 0 );
real_isr_ptr = DosProtToReal( (PVOID) real_isr );
if ( real_isr_ptr == 0 )
exit( 1 );
DosLockSegPages( SELECTOROF( real_isr ) );
DosLockSegPages( SELECTOROF( &Port ) );
DosSetRealProtVec( Port.interrupt_number,
(PIHANDLER) prot_isr,
real_isr_ptr,
(PIHANDLER far *) &Port.old_prot_vector,
(REALPTR far *) &Port.old_real_vector );
}
/*
* This routine does everything necessary to open the port so the
* main program can begin sending and receving characters. It sets
* up the UART with the correct data format, then sets up the
* interrupt service routines, and finally enables interrupts.
* At that point the UART can now being really doing something.
*/
enum port_names { COM1, COM2 };
int OpenComPort( enum port_names port_name,
unsigned int baud_rate,
char parity,
unsigned int word_length,
unsigned int stop_bits )
{
int temp;
switch ( port_name ) {
case COM1 :
Port.uart_address = 0x3f8;
Port.interrupt_number = 12;
break;
case COM2 :
Port.uart_address = 0x2f8;
Port.interrupt_number = 1;;
break;
default :
return 0;
}
Port.head_pointer = 0;
Port.tail_pointer = 0;
if ( ! SetComParameters( baud_rate, parity, word_length, stop_bits ) )
return( 0 );
SetInterruptVectors();
/*
* This section of code actually enables interrupts.
*/
temp = inp( 0x21 );
outp( 0x21, temp & ~Port.irq_mask );
outp( Port.uart_address + MODEM_CONTROL_REGISTER,
MCR_DATA_TERMINAL_READY + MCR_REQUEST_TO_SEND + MCR_OUT2 );
outp( Port.uart_address + INTERRUPT_ENABLE_REGISTER,
IER_RECEIVE_DATA_INTERRUPT );
return 1;
}
/*
* This short routine is used to send a character. All it does is wait
* for the UART to be ready to accept the character, then stuff it
* out the UART's transmit register.
*/
void SendChar( int c )
{
int lsr;
for ( ; ; ) {
lsr = inp( Port.uart_address + LINE_STATUS_REGISTER );
if ( lsr & LSR_THRE ) {
outp( Port.uart_address, c );
return;
}
}
}
/*
* This boolean routine lets the main program know whether or not
* characters are ready to be pulled out of the input buffer.
*/
int DataReady( void )
{
return( Port.head_pointer != Port.tail_pointer );
}
/*
* ReceiveChar reads in a single character from the input buffer, then
* updates the tail pointer and returns the character.
*/
int ReceiveChar( void )
{
int c;
if ( Port.head_pointer == Port.tail_pointer )
return( -1 );
c = Port.buffer[ Port.tail_pointer++ ] & 0xff;
Port.tail_pointer &= 1023;
return( c );
}
/*
* CloseComPort() disables interrupts, then restores the old interrupt
* vectors and unlocks memory. It also drops DTR and CTS.
*/
void CloseComPort( void )
{
int temp;
outp( Port.uart_address + INTERRUPT_ENABLE_REGISTER, 0 );
temp = inp( 0x21 );
outp( 0x21, temp | Port.irq_mask );
DosSetRealProtVec( Port.interrupt_number,
(PIHANDLER) Port.old_port_vector,
(REALPTR) Port.old_real_vector, 0, 0 );
DosUnlockSegPages( SELECTOROF( real_isr ) );
DosUnlockSegPages( SELECTOROF( &Port ) );
outp( Port.uart_address + MODEM_CONTROL_REGISTER, 0 );
}
/*
* The main program opens the port, then sits in a simple loop. The
* loop just sends keystrokes out the com ports, and copies input
* data to the screen. The only thing unusual is the provision for
* printing out the port data if the input character is a ^Z.
*/
main()
{
int c;
if ( !OpenComPort( COM1, 2400, 'N', 8, 1 ) ) {
printf( "Error opening port!\n" );
return 1;
}
for ( ; ; ) {
if ( kbhit() ) {
c = getch();
if ( c == 27 )
break;
else if ( c == 26 ) {
printf( "\nPort status:\n" );
printf( "UART address : %04.4x\n", Port.uart_address );
printf( "Head pointer : %04.4x\n", Port.head_pointer );
printf( "Tail pointer : %04.4x\n", Port.tail_pointer );
printf( "Old prot vector : %08.8Fp\n", Port.old_prot_vector );
printf( "Old real vector : %08.8Fp\n", Port.old_real_vector );
printf( "IRQ mask : %02.2x\n", Port.irq_mask );
printf( "Interrupt number : %02.2x\n", Port.interrupt_number );
printf( "Real int count : %04.4x\n", Port.real_count );
printf( "Prot int count : %04.4x\n", Port.prot_count );
} else
SendChar( c );
}
if ( DataReady() ) {
c = ReceiveChar();
putch( c );
}
}
CloseComPort();
return 0;
}