Contents of the LAB8250.DOC file
< This is lab material prepared for electronics students at
the New Community College of Baltimore. It is designed to
demonstrate the rudiments of programming the 8250 UART to
students with little or no programmming experence. It DOES
NOT include pinouts of the 8250.
nccb // 3-5-92>
Consider the following problems:
1) You have an automatic drill press that can be driven through an
RS-232 port at 6 bits, even parity, two stop bits, 420 baud. The
standard IBM BIOS doesn't support this baud rate.
2) You think your serial port is not working properly and you'd like
to test it.
3) You want to use an XT to as a terminal for an old mainframe that
requires "stick parity", meaning that the parity bit is always the
same as data bit #4.
These problems are all easily solved by writing a program that
directly accesses the serial port.
The sample program, written in BASIC, initializes a serial port,
writes to it and reads from it.
8250 ADDRESS/ REGISTER MAP
All addresses are relative to base address
address register name r/w? notes
TxD TxD - write
0 ----------(0)--- RxD RxD - read ( Toggled by )
\ | ( bit 7 of )
\ | ( LCR )
| \__ BAUD RATE DIVISOR LOW r/w
1 -----------(0)--- INTERRUPT ENABLE r/w ( Toggled by )
\ | ( bit 7 OF )
\ | ( LCR )
| \ __ BAUD RATE DIVISIOR HIGH r/w
2 ------------|------ INTERRUPT ID REGISTER r/w identifies
| source of
| bit 7
3 --------------------- LINE CONTROL REGISTER r/w used during
4 ---------------------- Modem Control Register r/w includes
5 ---------------------- Line Status Register r/w
6 ---------------------- Modem Status Register r/w
Line Control Register (Address Base + 03)
This register is used primarily during setup.
7 6 5 4 3 2 1 0
| | | | | | | |
| | | | | | |______|_____WORD LENGTH
| | | | | | SELECT
| | | | | |
| | | | | | WLS1 WLS0
| | | | | | 0 0 5 DATA BITS
| | | | | | 0 1 6 DATA BITS
| | | | | | 1 0 7 DATA BITS
| | | | | | 1 1 8 DATA BITS
| | |_____|_____| |
| | | |________________NUMBER OF
| | | STOP BITS
| | | 0 - 1 stop bit
| | | 1 - 2 stop bits
| | |
| | |___________________PARITY CONTROL
| | Bit 3 - parity enable
| | 0-no parity
| | 1-parity on
| | Bit 4 - 0- odd parity
| | 1- even parity
| | Bit 5 - "Stick Parity"
| | should be set to
| | 0 in modern
| | applications
| |_____________________BIT 6 - Send BREAK. Drives
| SOUT (DATA OUT) Low
|______________________DLAB - REGISTER POINTER
Toggles registers at address 0
1 - access to Baud Rate Divisors
0 - Access to TxD,RxD and Interrupt Enable
EXAMPLE : 10011110 (9Eh)- Access to BAUD RATE
DIVISORS, EVEN PARITY, 7 bits, 2 stop bits
LINE STATUS REGISTER
This register indicates the status of the TRANSMIT and RECIEVE
Bit 7 - Always reads 0
Bit 6 - Transmitter SHIFT REGISTER empty
Bit 5 - Transmitter holding register empty
When Bit 5 is high, the 8250 is ready to accept more data for transmission.
When Bits 5 and 6 are both high, the 8250 has finished sending all the data it
has been ordered to send.
Bit 4 - Break Interrupt
Bit 3 - Framing Error
Bit 2 - Parity Error
Bit 1 - Overrun Error
Bit 0 - Data Ready
When Bit 0 is high, there is data in the RxD register waiting to be read.
BAUD RATE DIVISOR REGISTERS
The 8250 decides its BAUD RATE according to the following formula:
BAUD RATE = ---------------
16 * DIVISOR
Since the standard clock rate on the 8250 in an IBM compatible computer
is 1.8432 MHz, two bytes are often needed to hold the divisor. BRDHI gets
the HIGH BYTE, BRDL0 the low byte.
TRANSMIT and RECIEVE DATA REGISTERS
These are the DATA REGISTERS, what the chip is all about. TxD is accessed
when writing, RxD when reading.
Access to the BAUD RATE DIVISORS and the DATA REGSTERS (as well as the
Interrupt Enable Register) is switched (toggled) by the DLAB pointer,
bit 7 of the LCR. This allows 9 registers to be addressed through 3 bits of
microprocessor address space, which is normally room for 8 registers. This
is an important concept.
INTERRUPT ENABLE REGISTER - Address 01
This register determines what conditions will activate the INTRPT pin,
which is usually used to generate an IRQ (interrupt request) at the
In our sample program all the bits of this register are set to 0,
since interrupts are not used.
INTERRUPT IDENTIFICATION REGISTER - Address 02
After an IRQ is generated, the INTERRUPT SERVICE ROUTINE (ISR) can read
this register to find the cause of the interrupt.
Not used in sample program.
MODEM CONTROL REGISTER
This register determines what happens on some of the control lines
between the 8250 and SERIAL PORT ( Modem or RS-232, etc.).
Bits 5, 6, and 7 are not used.
Bit 4 controls LOCAL LOOPBACK. When set, data sent out the
TxD register will appear in the RxD register. This is
useful for testing.
Bit 3, 2 control OUT1 and OUT2, auxilliary control lines.
Bit 1 - asserts RTS* when set
Bit 0 - asserts DTR* when set
* - ACTIVE LOW
DTR* and RTS* are the signals that are most useful here.
NOTE - the term MODEM is used to mean MODULATOR/DEMODULATOR, and may or may
not refer to a device connected to the phone lines.
MODEM STATUS REGISTER -
This register indicates the status of some of the control lines from
the serial port to the 8250. The most important are:
Bit 5 - DATA SET READY (DSR). In loopback mode, this is connected to
bit 0 (DTR) of the MODEM CONTROL REGISTER
Bit 4 - CLEAR TO SEND (CTS) connected to RTS (bit 0) of MCR when
in LOOPBACK MODE.
Note that even though the outputs (RTS*, DTR*) or inputs (DSR,CTS) are active
low on the pins of the 8250, the corresponding bits in the CONTROL and STATUS
registers are active high. This allows for uniform programming.
The MODEM CONTROL REGISTER (MCR) and MODEM STATUS REGISTER (MSR) may be
thought of as "handshake" registers.
NOTES on COMTEST.BAS
The program works by writing directly to the 8250 UART in IBM and
compatible PC's. Like most peripheral chips, the UART appears to the
microprocessor as a series of Input/Output ports. Access to the ports
is by use of the Assembly-language IN and OUT instructions, or their
equivalent in high level languages.
The BASIC program begins by intializing several constants used
in the program. Students may wish to experiment by changing the baud rate.
The constants LOOPBACK and HANDSHAKE have strictly Boolean value to the
program. They are either true (not equal to zero) or not true (zero).
The DELAYTIME constant is useful for modifications of the program that
do not check the line status register before reading or writing.
Line 260 clears the screen and turns off the reminder line for
for the function keys. Line 280 defines all variables as integers by
default. BASIC uses 16 bit integers, which is handy for working with
hardware. The only variable NOT defined as an integer is CLOCK#. The
pound sign (#) at the end of the variable name insures that BASIC treats
it as a double-precision number. This is necessary because the PC serial
port uses a standard clock speed of 1.8432 MHz - too large a value to be
defined as an integer, which can only range from -32,768 to +32,767.
BASIC takes care of the "type conversion" when doing mixed arithmetic
Lines 360-420 do some integer arithmetic to separate the 16-bit
baud rate divisor into its low and high bytes. The MOD operator does the
division and returns the remainder. It is the complement of the integer
division operator (\), which returns the dividend and throws out the
remainder. The formula for separating any 16-bit integer into low and high
LOW_BYTE = INTEGER MOD 256
HIGH_BYTE = INTEGER \ 256
In BASIC, the two values are still 16-bit integers, although neither of them
is larger than 255 (Hex 00FF). When a value is sent out a hardware port,
BASIC sends only the low byte.
Lines 620-640 get the address of COM1 from the operating system's
BIOS parameter area. Strictly speaking, this is not necessary, since
addresses for COM1 through COM4 are standard on IBM compatibles, but it is
easier to let the program find the addresses than to try and remember them!
The program is easily modified to work with COM2.
Lines 1000-2000 initialize the chip by writing the appropriate
value into the line control and modem control registers. These are all defined
relative to the base address of the 8250. The various registers of the 8250
are described elsewhere.
Sometimes it is desirable to change only one bit in one of the
control registers: in that case, the procedure is to read the register,
modify the result with an AND or OR instruction, and the write the value back
into the register. This is done in lines 1220-1260 and lines 1300-1320.
Similarly, sometimes it is desirable to test a particular bit
in a register: this is done by reading the regster, ANDing it with the
appropriate value, and testing to see if the result is TRUE or NOT TRUE.
A statement like IF (LINESTAT AND &H10) may seem like bad grammar, but it
is good programming; the BASIC interpreter finds the value of the variable,
performs the AND operation, and checks to see if the result is zero or not.
Lines 2000-2500 set up the screen display area. Lines 2500-3000 set up
a loop system where the program waits for a TxD BUFFER CLEAR in the line
status register and writes a byte to the TxD register (lines 2600-2620) and
then waits for a DATA READY in the line status register and reads RxD
It might be interesting to replace 2620 with
GOSUB 5000 and to replace 2680 with
In that case it will be necessary to add a delay loop after each GOSUB,
which will need to get longer as the baud rate gets slower. Experiment.
The block of code at 4000 performs the read operaton and writes the
data to the screen. The code at 5000 does the write operation.
Two possible modifications to the progam are to add handshaking, and
to use it to connect two computers together.
100 ' This program initialzes the 8250 UART and writes a test string
120 ' to t, as well as recieving a test strng. It can be used as a model
140 ' for other COM programs.
160 ' NOTE -- do not confuse the National Semiconductor 8250 series of
180 ' UARTS with the Intel 8251 series. They are completely different
200 ' chips.
240 ' Below are the parameters for the COMM port
260 CLS:KEY OFF
280 DEFINT A-Z
290 'Note the unusual baud rate below. Change this line to try other baud rates
340 ' standard pc clock rate at the serial port
380 BRDLO=BRDIVISOR MOD 256
400 ' low byte of baudrate divisor
440 ' High byte - \ means INTEGER division, no rounding, discard remainder..
480 LOOPBACK=NOT 0
490 ' change the line above to LOOPBACK=0 to send data out the serial port
520 'change the line above depending on the needs of the setup. See notes
560 ' The IBM pc series store the com port addresses at 0040:0 and
580 ' 40:02. The lines below get the address of com1. For com2, use a
600 ' PEEK at 02 and 03
620 DEF SEG = &H40
660 DEF SEG
1000 ' Now to initialize the com port
1020 ' First, disable interrupts
1040 OUT COMPORT+1,0
1060 ' Set line control registers for access to baud rate divisors,
1080 ' 7 bits, even parity (1000 1110)
1100 OUT COMPORT+3,&H8E
1120 ' set baud rate divisors. In Intel fashion, the low byte comes
1140 ' first, high byte second
1160 OUT COMPORT, BRDLO
1180 OUT COMPORT+1,BRDHI
1200 ' get line control registers, clear bit 7 for access to data registers.
1240 LINECTL=LINECTL AND &H7F
1260 OUT COMPORT + 3, LINECTL
1280 IF NOT LOOPBACK THEN 2000
1320 MODEMCTL=(MODEMCTL OR &H10):OUT COMPORT+4,MODEMCTL
1340 'the line above sets the local loopback bit in the modem control register
2020 ' below is the test string we use for output
2040 TEST$="This is a test of direct hardware access to the 8250 UART"
2080 ' now to send the test string
2120 ' first, divide the screen for a nice appearance....
2140 FOR I = 1 TO 80
2160 LOCATE 12,I
2180 PRINT CHR$(&HCD)
2200 NEXT I
2260 LOCATE 11,37:PRINT "output"
2280 LOCATE 13,37:PRINT "input"
2500 ' now, to go to work.....
2520 FOR I=1 TO LEN(TEST$)
2560 'de-commment delay loop below if needed
2580 'FOR J=0 TO DELAYTIME:NEXT J
2590 ' check the line status reg to see if the TxD buffer is clear.....
2620 IF (LINESTAT AND &H20) THEN GOSUB 5100 ELSE 2600
2640 'FOR J=0 TO DELAYTIME:NEXT J
2650 'check to see if data is waiting for a read......
2680 IF(LINESTAT AND 1) THEN GOSUB 4060 ELSE 2660
2700 'de-commment delay loop below if needed
2720 'FOR J=0 TO DELAYTIME:NEXT J
2740 NEXT I
2760 LOCATE 22,1
4000 'this is the READ DATA subroutine
4100 LOCATE 18,I:IN$=CHR$(INCHAR):PRINT IN$
4120 ' the line above converts the data and prints it - not a necessary
4140 ' step in assembly language or C!!
5000 'this is the SEND DATA subroutine
5120 OUT COMPORT,ASC(OUTP$)
5140 LOCATE 5,I
5160 PRINT OUTP$