8250 UART Programming
The HA-8-4 four-port serial card uses 8250 UART chips to provide asynchronous serial communications. This was a very popular chip, and is the same one used in early IBM PC's and compatibles.
This section will describe the 8250 chip and register addressing, the functions of each register, and will provide a short but detailed example of the use of the 8250 UART.
Be sure to read the Emulator-Specific Notes section, as we took a few shortcuts in the implementation of serial communication internal to the emulator.
Page Index |
8250 UART Addressing 8250 Registers Baud Rate Generation Example Emulator-Specific Notes |
The one-byte address of the 8250 must be evenly divisible by 8. Therefore the left-most five bits of the address selects a particular 8250 UART. The right-most three bits of the port address are passed directly to the 8250 chip and are used to select the specific 8250 register that is to be read or written. Because of multiple uses of registers, there are more than eight available, but all are numbered from zero through eight.
The register numbers given in the table below, map to the least-significant three bits of the I/O address.
Assuming these equates:
TTYPORT EQU 350Q H-19 console...you would access these ports as follows:
IN TTYPORT+UR.LSR Read line status registerThe 8250 UART program interface consists of 8 registers that can be read or written, two of which are multiple-purpose registers.
Mnemonic | Register Number |
Description |
---|---|---|
UR.RBR | 0 | Receiver buffer register - read only |
UR.THR | 0 | Transmitter holding register - write only |
UR.DLL | 0 | Divisor latch lsb |
UR.DLM | 1 | Divisor latch msb |
UR.IER | 1 | Interrupt enable register |
UR.IIR | 2 | Interrupt identification register |
UR.LCR | 3 | Line control register |
UR.MCR | 4 | Modem control register |
UR.LSR | 5 | Line status register |
UR.MSR | 6 | Modem status register |
UR.SCR | 7 | Scratch register |
Detailed descriptions follow.
Register 0 is a multiple-purpose register. When the divisor latch is off and the register is read, it returns the most recent byte received on the serial interface.
A value will always be returned when you read this register; you should check the line status register to see if a new byte has been received prior to reading UR.RBR.
Register 0 is a multiple-purpose register. When the divisor latch is off, a byte written to the register is treated as output data.
What actually happens depends on the state of the transmitter holding register and transmitter shift register:
UR.LSR Values | ||
---|---|---|
UC.THE | UC.TSE | Action |
1 | 1 | UC.THE is set to 0. The byte is immediately transferred to the shift register, and UC.THE transitions to 1. If transmitter interrupts are enabled, there will be an immediate interrupt. |
1 | 0 | The byte will sit in the transmitter holding register until the previous byte is completely written to the serial interface. If another byte is written while the transmitter holding register is full, the new byte will overwrite the old one with no error indication. Always check UC.THE before writing. |
0 | 0 | There was a byte being shifted onto the serial interface and one waiting. The new byte will overwrite the old one waiting in the transmitter holding register. Always check UC.THE in the line status register before writing. |
Register 0 is a multiple-purpose register. When the divisor latch is on and the register is written, the least significant byte of the baud rate divisor latch is set. This register may be read to determine the lsb of the baud rate divisor.
Register 1 is a multiple-purpose register. When the divisor latch is on and the register is written, the most significant byte of the baud rate divisor latch is set. This register may be read to determine the msb of the baud rate divisor.
Register 1 is a multiple-purpose register. When the divisor latch is off and the register is written, the output byte changes the conditions under which the 8250 will generate interrupts. The register may also be read to determine what conditions are currently set.
Values:
Mnemonic | Value | Description |
---|---|---|
UC.EDA | 00000001B | Enable received data available interrupt - when UC.DR transitions from 0 to 1, this interrupt is generated. |
UC.TRE | 00000010B | Enable transmitter hold register empty interrupt - when UC.THE transitions from 0 to 1, this interrupt is generated. |
UC.RSI | 00000100B | Enable receiver status interrupt - when UC.OR, UC.PE, UC.FE, or UC.BI transitions from 0 to 1, this interrupt is generated. |
UC.MSI | 00001000B | Enable modem status interrupt - when UC.DCS, UC.DDR, UC.TER, or UC.DRL transitions from 0 to 1, this interrupt is generated. |
When the 8250 UART signals an interrupt, this register provides the interrupt ID, or the cause of the interrupt condition.
Mnemonic | Value | Description |
---|---|---|
UC.IIP | 00000001B | Interrupt pending (inverted - 0 means pending) |
UC.IID | 00000110B | Interrupt ID field |
ID value | 00000110B | Line status |
00000100B | Received data available | |
00000010B | Transmitter holding register empty | |
00000000B | Modem status |
The ID's are listed in priority order, from highest priority to lowest.
Interrupt conditions will remain on until cleared; they are cleared as follows:
Reading the UR.LSR port will clear the interrupt condition.
Read UR.RBR to clear the interrupt condition.
Simply reading the UR.IIR register is sufficient to clear this interrupt condition; you do not have to write data to the transmitter buffer.
Read UR.MSR to clear the interrupt condition. The delta bits will all be zeroed upon reading UR.MSR.
Description
Mnemonic | Value | Description |
---|---|---|
UC.5BW | 00000000B | Use 5 data bits (effectively obsolete) |
UC.6BW | 00000001B | Use 6 data bits (effectively obsolete) |
UC.7BW | 00000010B | Use 7 data bits |
UC.8BW | 00000011B | Use 8 data bits |
UC.2SB | 00000100B | Use two stop bits |
UC.PEN | 00001000B | Enable automatic parity calculation |
UC.EPS | 00010000B | Calculate even parity (off = odd parity) |
UC.SKP | 00100000B | Force parity to '1' |
UC.SB | 01000000B | Send break - hold the output at a spacing condition (break) until this bit is reset |
UC.DLA | 10000000B | Divisor latch access - ref UR.DLL, UR.DLM |
The most common data bit / parity combinations are:
When you send a break, you should ensure that the spacing condition remains on the interface for a minimum of 300ms. This timing is up to your program, not to the 8250 UART.
Description
Mnemonic | Value | Description |
---|---|---|
UC.DTR | 00000001B | DTE port: raise DTR DCE port: raise DSR |
UC.RTS | 00000010B | DTE port: raise RTS DCE port: raise CTS |
UC.OU1 | 00000100B | Raise the OU1 signal - looped back to RI (pin 21) on the DTE interface for port test) |
UC.OU2 | 00001000B | Raise the OU2 signal - tied to carrier (pin 8) on a DCE port so the H8 could source carrier detection |
UC.LOO | 00010000B | Enable diagnostic loopback - bytes written to UR.THR will be transmitted into UR.RBR |
Description
Mnemonic | Value | Description |
---|---|---|
UC.DR | 00000001B | Data ready - read UR. RBR |
UC.OR | 00000010B | Overrun error - data was not read in time and incoming data overwrote previous data |
UC.PE | 00000100B | Parity error - the parity generator noted a parity error |
UC.FE | 00001000B | Framing error - the character wasn't framed by a proper stop bit (may be due to break) |
UC.BI | 00010000B | Break indication - there is a break condition (constant space) on the received data line |
UC.THE | 00100000B | Transmitter holding register empty - ok to send another character now |
UC.TSE | 01000000B | Transmitter shift register empty - 8250 transmitter is now idle |
Note that an incoming break signal will normally manifest itself as a framing error with a null (000Q) in the receiver buffer; after sufficient time has elapsed the UC.BI bit should be set.
Description
Mnemonic | Value | Description |
---|---|---|
UC.DCS | 00000001B | Delta clear to send - there was a transition (0-1 or 1-0) in the clear to send lead (RS-232 pin 5) |
UC.DDR | 00000010B | Delta data set ready - there was a transition (0-1 or 1-0) in the data set ready lead (RS-232 pin 6) |
UC.TER | 00000100B | Trailing edge of ring indication - the ring indicator lead (RS-232 pin 21) transitioned from 1-0 |
UC.DRL | 00001000B | Delta receive line signal detect - the carrier signal lead (RS-232 pin 8) transitioned from 0-1 or 1-0. |
UC.CTS | 00010000B | Clear to send signal up = 1 Clear to send signal down = 0 |
UC.DSR | 00100000B | Data set ready signal up = 1 Data set ready signal down = 0 |
The scratch register is a one-byte storage location. You may write a byte to it and read it back. (This may have some value in certain low- or no-memory embedded controller applications, but it certainly has no application here.)
The 8250 UART uses a 1.8432 mhz clock to generate serial baud rates. This clock is divided by a 16-bit divisor stored in the UR.DLL and UR.DLM registers. The result of that division is a clock signal that is 16 times the desired baud rate. Example:
1,843,200 / 12 = 153,600 (12d = 014Q; see table below)
153,600 / 16 = 9600 baud
The 8250 will take 16-samples during one bit time when looking for a start bit, and will sample every 16 clock cycles thereafter to determine each bit value.
Divisors follow to generate common baud rates.
Divisor | |||
---|---|---|---|
Baud Rate | LSB | MSB | EQU Value |
75 | 000 | 006 | 6000A |
150 | 000 | 003 | 3000A |
300 | 200 | 001 | 1200A |
600 | 300 | 000 | 0300A |
1200 | 140 | 000 | 0140A |
2400 | 060 | 000 | 0060A |
4800 | 030 | 000 | 0030A |
9600 | 014 | 000 | 0014A |
19200 | 006 | 000 | 0006A |
38400 | 003 | 000 | 0003A |
Following are three sample subroutines: one to intialize the 8250 UART, one to read data from the chip, and one to write data to the chip.
PORT EQU 340Q Port baseThe emulator provides a very complete implementation of the 8250 UART, and all most functions documented above either work as documented or, where they provide no function, can be used with no harm.
The emulator has no need for baud rates and dataset signals, so programs written strictly to be run on the emulator do not need to bother with them.
The emulator ignores the selected number of data bits and parity; effectively, you get 8-bits, no parity, no matter what you select.
All interrupts are supported. However, understand the issues raised in features before using any interrupt on any port, other than receiver interrupts on the console port.
Incoming break from the console port (user pressed the F12 key) will be signaled by setting UC.BI and UC.FE, and a null (000Q) will be loaded into the receiver buffer, so you can detect break using either the break indicator or the framing error/null method.
If you send break on the modem port, the emulator will see to it that the break condition is held on the line for 300ms; you need only set/reset UC.SB.
You will never see a transition in the transmitter shift register. Serial transmission inside the emulator is immediate and takes no time, so the transmitter holding register is generally always empty. However, in some cases it's used as an internal flow-control mechanism - for example, the H-19 process controls UC.THE to hold output to a maximum of 2,000 cps. So do make sure to check UC.THE before writing, as in the example.