ACIA 6551

6551 RS232 port circuit.

The connector on the top left of the diagram is from my own 6502 boards (see sbc project) and is as it is for two reasons. It’s easy to wire on a stripboard layout and I have a lot of 26 way ribbon, headers and plugs. All the signals are directly from the 6502 except /SEL0 and /SEL1 which are used to select the block $F0xx with /SEL0 = 0 and /SEL1 = 1. The 10 pin IDC plug is to allow use of a standard PC type COM port plug. This was done because it’s easier to wire on stripboard and allows the choice of a 9 or 25 pin connector.

The two capacitors are low ESR electrolytics and are placed near the 6551 and near the MAX232. If you don’t have this type to hand you can use standard electrolytics with some low value ceramic capacitor, say 0.1uF, in parallel.

The 6551 occupies the 256 byte block $F0xx and, as the 6502 this was made for is clocked at 1.832MHz, is clocked by the phase 2 clock. If a different processor clock is being used then the 6551 can be clocked by a 1.832MHz oscillator as shown in the alternate clock diagram. Two jumpers are used to select which interrupt the 6551 is tied to. If interrupts are not required then both jumpers can be left open.

The MAX232 performs level translation between the TTL levels of the 6551 and the bipolar RS232 levels. Only the signals for a 5 wire interface are implemented with DCD and DSR being tied low on the 6551 to allow continuous operation.

The GAL16V8A is optional and is used purely to generate the chip select. This was not fitted to the original because it was not necessary to fully decode this part. For anyone interested the equations for this chip are in acia_01.pld and can be compiled with WinCUPL. The fuse file, acia_01.jed is also included.
The finished board

Software
To use the 6551 as a polled device the software is fairly simple. First the 6551 must be reset and the baud rate, character length and parity must be set.

; set the 6551 register addresses

A_rxd			= $F000	; ACIA receive data port
A_txd			= $F000	; ACIA transmit data port
A_sts			= $F001	; ACIA status port
A_res			= $F001	; ACIA reset port
A_cmd			= $F002	; ACIA command port
A_ctl			= $F003	; ACIA control port

; initialise 6551 ACIA

	STA	A_res		; soft reset (value not important)
	LDA	#$0B		; set specific modes and functions
				; no parity, no echo, no Tx interrupt
				; no Rx interrupt, enable Tx/Rx
	STA	A_cmd		; save to command register
				; all the following 8-N-1 with the baud rate
				; generator selected. uncomment the line with
				; the required baud rate.
;	LDA	#$1A		; 8-N-1, 2400 baud
;	LDA	#$1C		; 8-N-1, 4800 baud
	LDA	#$1E		; 8-N-1, 9600 baud
;	LDA	#$1F		; 8-N-1, 19200 baud
	STA	A_ctl		; set control register

To send a character all we need do is wait for the Tx buffer to empty then write the byte.

; wait for ACIA and Tx byte

	PHA			; save A
LAB_WAIT_Tx
	LDA	A_sts		; get status byte
	AND	#$10		; mask transmit buffer status flag
	BEQ	LAB_WAIT_Tx	; loop if tx buffer full

	PLA			; restore A
	STA	A_txd		; save byte to ACIA data port

To receive a character we need to wait for the Rx buffer to fill then read the byte.

; wait for ACIA and Rx byte

LAB_WAIT_Rx
	LDA	A_sts		; get ACIA status
	AND	#$08		; mask rx buffer status flag
	BEQ	LAB_WAIT_Rx	; loop if rx buffer empty

	LDA	A_rxd		; get byte from ACIA data port