From SaturnWiki
Jump to: navigation, search

Understanding the hardware

The ALDL port on OBDI saturns uses 8192 baud communications for communication. The data format is standard 8 data bits, 1 stop bit and no parity. However unlike a standard RS-232 port the voltage levels are around +/- 5v where the standard RS-232 is around +/- 12v. Due to this fact you will need a level converter to communicate with the PCM. Commercial cables are out there, but they are not much more than This DIY cable. Additionally the ALDL port puts both Receive and Transmit on the same wire.


Now that we have a proper cable, lets look a little more at the details of the communication protocol. starting with the data/stop/parity stuff.


The diagram above shows 8-N-1. There is one start bit, 8 data bits and 1 stop bit. This is how the equipment on either end knows the start and end of a byte.


The most important variable for communicating with another device is the data speed, or baud rate. The PC BIOS usually controls this, however the BIOS usually only supports 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600 and 115200 (assuming the 16550 or better UART is being used). well those speeds will not do for 8192. This is where UART clock divisor comes in. The standard clock rate for the 16500 is 115200hz this needs to be divided to achieve a usable speed. For this we use a divisor of 14. Which puts the baud around 8229 which leaves only a difference about 36, this is a small enough difference that the start and stop bit cycle will keep everything in sync.

Port Pins

The standard RS-232 port has more functions than just the data wires. but the data pins are the only ones used for the 8192 interface. For reference, the pin assignments for the standard port is below.

Signal DB-25 DB-9 connector
Carrier Detect DCD 8 1
Received Data RD 3 2
Transmitted Data TD 2 3
Data Terminal Ready DTR 20 4
Common Ground G 7 5
Data Set Ready DSR 6 6
Request To Send RTS 4 7
Clear To Send CTS 5 8
Ring Indicator RI 22 9

UART registers

The UART (Universal Asynchronous Receiver and Transmitter) is the heart of the serial interface. Understanding how to control this device at a lower level than the BIOS is key to communicating with 8192 baud devices. This is handled through a set of hardware registers or memory addresses that can be read or written to control how the UART functions. The table below lists the common register addresses of the first two serial ports.

Register Ports
Read mode Write mode First port Second port
Receive holding Transmit Holding/Divisor LSB 0x3F8 0x2F8
Interrupt Status FIFO control 0x3FA 0x2FA
N/A Line Control 0x3FB 0x2FB
N/A Modem Control 0x3FC 0x2FC
Line Status N/A 0x3FD 0x2FD
Modem Status N/A 0x3FE 0x2FE
Scratchpad Scratchpad 0x3FF 0x2FF

The first two registers are dual purposed for write mode, the mode of these two is controlled by the MSB of the Line Control Register. When the bit is set high the Divisor register is accessible when it is low the first register will be the Receive and Transmit Holding Registers and the second register will have no function.


This is where the data speed is controlled, this register spans across two addresses due to the fact that this register is 16 bits wide. In order to access this register the eighth bit of the Line control register needs to be set high. after that the register can be programmed via the first two addresses. "1111111111111111" = 65536 (or the speed 115200/65536=1.7baud) "0000000000000001" = 1 (or the speed 115200/1=115200)

Receive Holding

When the Divisor Access bit is NOT set this register holds the last byte received (non-FIFO) or the las byte not read (FIFO)

Transmit Holding

When the Divisor Access bit is not set this register will send it's contents on the wire.

Interrupt Enable

Bit Purpose
0 Receiver Ready
1 Transmitter Empty
2 Receiver Line Status
3 Modem Status Register
4 N/A
5 N/A
6 N/A
7 N/A

Interrupt Status

Bit Purpose
0 Interrupt pending
1 0=Modem status change, 0=Transmitter holding register empty, 0=Received data available, 0=Line status change, 1=Character timeout (16550)
2 0=Modem status change, 0=Transmitter holding register empty, 0=Received data available, 0=Line status change, 1=Character timeout (16550)
3 0=Modem status change, 1=Transmitter holding register empty, 0=Received data available, 1=Line status change, 0=Character timeout (16550)
4 N/A
5 N/A
6 0=No FIFO, 1=Unusable FIFO (16550 only), 1=FIFO enabled
7 0=No FIFO, 0=Unusable FIFO (16550 only), 1=FIFO enabled

FIFO Control

Bit Purpose
0 Enable/Disable FIFO
1 Set high to clear Receive Buffer
2 Set high to clear Transmit Buffer
3 Select DMA mode 0 or 1
4 N/A
5 N/A
6 0=one byte buffer, 1=four byte buffer, 0=eight byte buffer, 1=fourteen byte buffer
7 0=one byte buffer, 0=four byte buffer, 1=eight byte buffer, 1=fourteen byte buffer

Line Control

Bit Purpose
0 0=five data bits, 1=six data bits, 0=seven data bits, 1=eight data bits
1 0=five data bits, 0=six data bits, 1=seven data bits, 1=eight data bits
2 0=one stop bit, 1=1.5(for 5 data bits) or 2(for the rest) stop bits
3 Disable/Enable parity
4 0=odd parity, 1=even parity, 0=high parity, 1=low parity
5 0=odd parity, 0=even parity, 1=high parity, 1=low parity
6 Disable/Enable Break signal
7 Divisor Access

Modem Control

Bit Purpose
0 Data terminal ready
1 Request to send
2 Auxiliary output 1
3 Auxiliary output 2
4 Loopback mode
5 N/A
6 N/A
7 N/A

Line Status

Bit Purpose
0 Data available
1 Overrun error
2 Parity error
3 Framing error
4 Break signal received
5 Transmit Holding is empty
6 Transmit Holding is empty, and line is idle
7 Errornous data in FIFO

Modem Status

Bit Purpose
0 Clear to send has changed
1 Data set ready has changed
2 Ring indicator has changed
3 Carrier detect has changed
4 Clear to send
5 Data set ready
6 Ring indicator
7 Carrier detect


This is a painfully useless register, never seen a use for it aside from testing for an XT style UART. And if you are using an XT, your PCM may just be faster!

Using Qbasic to interface

Okay, I know, Qbasic WTF!?!?! This is the only programming (The term is used lightly) language I have access to for windows. Or care to for that matter, I do not own a current Windows system, nor have I owned one since 1995 when I beta tested Win95 (Yes, It was that bad!)

The key functions you will be needing is OUT and INP, they do pretty much the same thing in QBasic as they do in X86 ASM.

Code: Reading and Writing Hardware Ports
%byte = INP(&H3F8) '(this reads a byte from the Receive Holding Register)
OUT &H3F8, %byte '(this writes that byte back to the Transmit Holding Register)
Code: initialize the first port for 8192baud 8,n,1
OUT &H3FB, 131
OUT &H3F8, 14
OUT &H3F8, 0
OUT &H3FB, 3

In order to read and write data you will need to use the registers directly with the INP and OUT functions. Using the Transmit Buffer Empty flag will be a good idea when sending data, and using a FIFO is suggested.

Using Linux to interface

Linux makes this type of thing easy, considering BIOS is generally a last resort for anything with the Linux kernel.

From the command line (as root) just run "setserial /dev/ttyS0 baud_base 115200 divisor 14 spd_cust" or change /dev/ttyS0 to the port of your choice, then open the port at 38400.

Now you should be able to communicate at 8192 just as you would anything else using a serial port.

Using PalmOS to interface

Things are a little different here, the PalmOS devices do not use a standard UART and at that not all PamlOS devices use the same UARTS. The older devices are based on the DragonBall processors from Motorolla. Some of the later devices use ARM and even more exotic microcontrollers. As I get started on this, I will be looking at the older DB based devices. At first glance the UART divisor registers in these devices are only 3 bits. However the divisor is based on the main clock, leaving the option of altering the primary clock rate and then using a divisor to drop the UART speed right on 8192 baud.