(clarified by Brian Tristam Williams from an article by Mack McCormick) TI-99/4A RS232 Interface Technical Information ---------------------------------------------- The TMS9902 'Asynchronous Communications Controller' is the UART responsible for serial communications in the TI-99/4A. This information will assist programmers of the TI-99/4A who are interested in programming the RS232 interface peripheral directly; that is, in assembly. This may be necessary for applications such as terminal emulators, BBS software, file transfer utilities using transfer protocols, etc. Going directly to the mountain eliminates the frustration, complexity and SLOWNESS of dealing with the RS232's extremely limited built-in Device Service Routine (DSR) ROM. The utilities contained on this ROM lack the ability to detect the presence of a carrier, a ring indicator, or even the arrival of a new character. The chip you need to talk to is the TMS9902 Asynchronous Communications Controller, and to do this you will need an overview of the Communications Register Unit (CRU) bits assigned to the RS232 interface. TMS 9902 Overview: ______ | \/ | /Int |01 18| +5V | | XOut |02 17| /CE | | RIn |03 16| Clk (phase 3) | | CRUIn |04 15| CRUClk | | /RTS |05 14| S0 | | /CTS |06 13| S1 | | /DSR |07 12| S2 | | CRUOut |08 11| S3 | | Gnd |09 10| S4 |______| This NMOS-technology IC uses TTL level signals on all inputs and outputs. The device is capable of handling asynchronous communications with the following formats: - Word length: 5, 6, 7 or 8 - Number of stop bits: 1, 1,5 or 2 - Parity: Odd, Even or None - Bit rate: 75 to 19 200 The 9902 has built-in bit-rate generation, eliminating the complexity and expense of having them external to the chip. Also included is an interval timer which operates at resolutions from 64 to 16 320 microseconds. Interfacing between the 9902 and the 9900 host environment is done through the five CRU address lines, S0 to S4. When /CE (chip enable) is asserted, the 9902 is placed on the bus, and the function requested is determined by the CRU lines and control logic. CRUClk provides the CRU timing, while the system Clk input provides the timing for the rest of the chip. CRUIn and CRUOut are the CRU input and output bits. In addition to the CRU logic, additional lines are provided for /RTS (Request to Send), /CTS (Clear to Send) and /DSR (Data Set Ready). The received data is on RIn, the transmitted data on XOut, and the 9902 interrupt line is on /Int. Finally, power is supplied on the +5V and Gnd pins. To convert from the TTL-level signals used by the 9902 to standard RS232-C line voltages, the prolific 75188/75189 driver/receiver pair is used. These drive the XOut/RIn signals, as well as DSR, RTS and CTS (even though /RTS and /CTS are tied together in the TI-99/4A's RS232 peripheral). The 9902 provides interrupts (logic LOW on /Int) when it needs attention - this will be explained later. Programming the 9902 requires the use of the CRU instructions TB, SBO, SBZ, LDCR and STCR. CRU Input Bit Assignments ------------------------- >1F: Interrupt - when programmed, any of the conditions DSCInt, TimInt, XBInt and RBInt, if met, will set this bit and also cause the /Int pin on the 9902 to be asserted. In addition, the respective bit that raised this condition will be set. >1E: Flag - Set when any of the load register commands are issued or when the break bit is set (See Table 2.) >1D: Data Set Status Change. Set when either DSR or CTS changes logic state (the state-change is confirmed when the line is stable for 2 clock cycles). This bit is cleared when an output to CRU bit >0F (DSCEnb) is made. >1C: Clear To Send. Indicates the status of the CTS pin. Note that the bit present will be the inverse of the logic level on CTS pin. >1B: Data Set Ready. Indicates the status of the DSR pin. Note that the bit present will be the inverse of the logic level on DSR pin. >1A: Request To Send. Indicates the status of the RTS pin. Note that the bit present will be the inverse of the logic level on DSR pin. >19: Timer Elapsed. Set when the 9902's timer register equals 0. Cleared by an output to bit >14 (TimEnb). >18: Timer Error. Set when the timer has reached 0 while TimElp is set. Raised when the software has failed to recognize TimElp and reset it before another interval passed. Cleared by an output to bit >14. >17: Transmitter Shift Register Empty. Set when the Transmitter shift register is not currently sending data. When the bit is set, XOut will be in a logic '1' state. When not set, character transmission is in progress. >16: Transmit Buffer Register Empty. When set, this indicates that there is not a character waiting to be transmitted. It is set to 0 when a character has been written to the transmit buffer register. >15: Receive Buffer Register Loaded. When set, this indicates that a complete character has been assembled in the receive buffer register. It is cleared by an output to bit 18 (RIEnb). >14: Data Set Status Change (Interrupt). Set when either DSR or CTS changes state and the Data Set Status Interrupt has been enabled. >13: Timer Interrupt. Set when the timer has elapsed and the Timer Interrupt has been enabled. >11: Transmitter Interrupt. Set when the Transmit buffer register is empty and the Transmitter Interrupt has been enabled. >10: Receive Interrupt. Set when the Receive Buffer register is loaded and the receive interrupt has been enabled. >0F: Receive Input. The logic level of the RIn line at the time that the input was sampled. >0E: Receive Start Bit Detect. Set during transition of the first bit of a word. Normally not used. >0D: Receive Full Bit Detect. Set when the first bit of the character (excluding the start bit) is received, and is cleared when the character is completed. Not normally used. >0C: Receive Framing Error. Set when the stop bit of the received character is a '0' (should be a logic '1'), indicating a transmission error. Reset when a character with a correct stop bit is received. It is recommended that this bit only be sampled when RBRL is set. >0B: Receive Overrun Error. Set when another character is being assembled in the 9902 and the previous character has not been acquired from the 9902 by the software, and the RBRL flag reset. >0A: Receive Parity Error. Set when the parity of the incoming character is incorrect, indicating transmission error. Reset when a character with correct parity is processed. >09: Receive Error. Set when RFER, ROVER, or RPER is set. >07->00: Receive Buffer Register. This is the register where the received character is stored. For data lengths shorter than eight bits the data is right-justified in the register. CRU Output Bit Assignments -------------------------- >1F: Reset. Setting this bit high causes the entire chip to reset. All interrupts are disabled, RTS is set high, all register flags (LdCtrl, LDIR, LRDR, LXDR) are set high and the Break flag is reset. No operation to the TMS 9902 should be performed for 11 clock cycles after a reset. >1E->16: Not used. >15: Data Set Change Interrupt Enable. Setting this bit causes the 9902 to generate an interrupt when input bit >1D (Data Set Status Change) is set. Resetting this bit disables the DSCH interrupt. Writing any value to this bit will reset input bit >1D. >14: Timer Interrupt Enable. Setting this bit causes the 9902 to generate an interrupt when TimElp (Input bit >19) is set. Resetting this bit disables the TimElp interrupts. Writing any value to this bit will reset input bit >19. >13: Transmit Buffer Interrupt Enable. Setting this bit causes the 9902 to generate an interrupt when XBRE (input bit >16) is set. Resetting this bit disables XBRE interrupts. This bit dos not affect the current value of XBRE. >12: Receiver Interrupt Enable. Setting this bit causes the 9902 to generate an interrupt when RBRL (input bit >15) is set. Resetting this bit disables RBRL interrupts. Wrtiing any value to this bit causes RBRL to reset. >11: Break On. Setting this bit causes the XOut line to go to logic zero whenever the transmitter is active and XBR and XSR (both transmitter buffer and shift registers) are empty and inhibits loading characters into the transmit buffer register. Resetting this bit causes the transmitter to return to normal operation. >10: RTS On. Setting this bit causes the RTS handshake signal to be active (low.) Resetting this bit causes RTS to go high after XSR and XBR are empty and BRKON is reset. (RTS will not go inactive until a character has completed tansmission.) >0F: Test Mode. Setting this bit causes RTS to be internally connected to CTS, XOUT to RIN, DSR held internally low, and the interval timer to operate at 32 times its normal rate. This is useful for debugging programs that are transmitting and receiving data and for testing to ensure that the internal 9902 functions are working properly. Resetting this bit causes the 9902 to return to normal operating mode. Register Load Control Flags: >0E: Load Control Register. This bit, when set (either explicitly or after the RESET bit is set), causes the control register in bits 0-7 to be loaded into the 9902. Resetting this bit, or upon reception of the last control register bit, causes the control register to be inhibited from loading. >0D: Load Interval Register. Setting this bit causes the next 8 bits (bits 0-7) to be loaded into the 9902's interval register. the Bit is reset when the last data bit is loaded or a logic zeo is written to LDIR. To load this regiter, LDCTRL must be reset (See Table 3.) >0C: Load Receive Data Rate Register. Setting this bit causes the next 11 bits (0-10) to be loaded into the 9902's Receive data rate register. The bit is reset when the last data bit is loaded or a logic zero is written to LRDR. To load this register LDCTRL and LDIR must be zero. >0B: Load Transmit Data Rate Register. Setting this bit causes the next 11 bits (0-10) to be loaded into the 9902's Transmit data rate register. The bit is reset when the last data bit is loaded or a logic zero is written to LXDR. To load this register LDCTRL and LDIR must be zero. Note: If both the transmit and receive data rates are the same both registers can be loaded simultaneously by setting both LRDR and LXDR. >0A - >00: Data. Depending on the control bits set data rate registers, the interval register, the control register or data to be transmitted are written to these bits. The Control Register: The control regiser is used to define the character size, the parity of the word, the number of stop bits and the operating frequency of the 9902. Table 4 outlines the bit positions and their settings. Of importance is bit 3: for the /4A environment this bit should always be zero. The PENB bit determines if parity checking is done. If set, parity checking is done; if reset, no parity checking is done. The PODD bit determines whether the 9902 will check for/generate odd parity; if set, odd parity is processed; if reset, even parity is processed. The Data Rate Registers: Both receive and transmit registers share the same format. The data rate registers are basically two programmable counters that divide the system clock depending on the bit settings in the counters to derive the data rate. The first counter is a divide-by-8 counter that is active if bit 10 of the data rate register is loaded. Since the internal 9902 clock frequency is 1 MHz, setting this bit causes its output to be 125KHz, while resetting this bit passes the system clock right through. The output of this first counter is fed into a 10-bit counter and is further divided by a value from 0 to 1023. The output from this counter is actually twice the data rate (interval for half the bit period). So this output is divided by two to get the actual data rate. Since this formula is not exactly the easiest way to get the baud rate, here's a list of the more popular speeds and the corresponing register values: Speed Hex Value ===== ========= 110 bps >400 + >238 300 bps >400 + >0D0 600 bps >341 1200 bps >1A1 2400 bps >0D0 4800 bps >068 9600 bps >034 (Note: the >400 is the divide-by-8 counter enable. If you need an oddball baud rate, the formula is F = Fint (1 MHz) -------------------------- bps 2 * (D9-D0) * (8 D10) where the value of D9-D0 is between 0 and 1023, inclusive, and D10 is the divide-by-8 counter.) The Interval Register: The Interval register is an 8-bit counter which is decremented every 64 clock cycles. When the interval counter reches zero, the appropraite interval register flags are set, and if interval interrups have been enabled, the 9902 will generate an interrupt. Taking into account the register size, the interval counter has resolution from 64 to 16,320 microseconds in 64-microsecond increments. Programming The TMS9902: This seems like an awful lot of stuff to swallow in order to program the 9902, but in reality, a lot of the nasty things are taken care of by the chip. Before we take a look at some code examples, we need to know something about the way the 9902 is addressed in the /4A environment. Most of you who have gotten the disassembly listing of the RS232 DSR know that the DSR code is at CRU address >1300. The two 9902s are at +>40 and +>80, respectively, so the values used for 9902 accesses are >1340 and >1380. If you have the additonal RS232 card, its CRU offset is >1500 and the 9902s are also at +>40 and >+80. It's suggested that when you code your routines, you have a subroutne which sets up the base address to turn the DSR on and then adjusts R12 to access the proper 9902. Interrupts in the RS232 DSR: Tracing through the disassembly listing reveals that, during an interrupt request from the RS232 DSR, it determines which device fired the interrupt and whether or not a character was received. If a character was not received, it RESETS the 9902 - so be careful how you program the 9902 in the /4A environment. As a side benefit, if you use interrupts, the DSR provides you with a mechanism for getting incoming characters and buffering them. Carefully trace the interrupt code and you'll find that >8300 contains a VDP address and >8302 and >8304 are pointers that are manipulated to place characters into a queue in VDP RAM. Example: To give you an idea of what goes on with the 9902, Assembly source code is being provided that shows how the 9902 is initialized and how characters are transmitted and received. The program is the infamous T99 dumb terminal emulator I wrote over a year ago that uses the first RS232 port for a modem running at 300 baud, 1 stop bit and even parity with a printer attached to port 2, running at 9600 bps at odd parity and 1 stop bit. The printer is turned on by receipt of a control-R and is shut off by receipt of a control-T. This code also shows you some of the things that are involved in writing a terminal emulator and can serve as a basis for writing more powerful emulators. The T99 source code can be found in DL2 in the file T99.ASM. Summary: There are still a lot of things that we didn't cover, in regards to the 9902, but, with the information we have provided here, you should be in a postion to do some exploring on your own, such as using one or two of the control lines to hook up with modems that provide auto-answer/auto-dial capabilites and write a assembly program to get you those features. The rest is up to you and your imagination!