NOTE: The ACIA will _always_ generate an interrupt when a change of state occurs on either the DCD or DSR line (unless the lines are not connected in the device's cable).
The 6551 ACIA was chosen for several reasons, including the low cost and compatibility with other Commodore (MOS) integrated circuits. Commodore used the 6551 as a model for the Kernal software. Control, Command, and Status registers in the Kernal routines partially mimic their hardware counterparts in the ACIA.
NOTE: If you're using the Kernal software registers in your program, be sure to review the enclosed 6551 data sheet carefully. Several of the hardware- register locations do _not_ perform the same function as their software counterparts. You may need to make a few changes in your program to accommodate the differences.
If your program already directly manipulates the Kernal RS-232 buffers, you'll find it very easy to adapt to the ACIA. If you use calls to the Kernal RS-232 file routines instead, you'll need to implement lower-level code to get and store buffer data.
Briefly, each buffer has a "head" and "tail" pointer. The head points to the next byte to be removed from the buffer. The tail points to the next free location in which to store a byte. If the head and tail both point to the same location, the buffer is empty. If (tail+1)==head, the buffer is full.
The interrupt handler described below will place received bytes at the tail of the receive buffer. Your program should monitor the buffer, either by comparing the head and tail pointers (as the Commodore Kernal routines do), or by maintaining a byte count through the interrupt handler (as the attached sample does). When bytes are available, your program can process them, move the head pointer to the next character, and decrement the counter if you use one.
You should send a "Ctrl-S" (ASCII 19) to the host when the buffer is nearing capacity. At higher baud rates, this "maximum size" point may need to be lowered. We found 50 to 100 bytes worked fairly well at 9600 baud. You can probably do things more efficiently (we were using a _very_ rough implementation) and set a higher maximum size. At some "maximum size", a "Ctrl-Q" (ASCII 17) can be sent to the host to resume transmission.
To transmit a byte using the logic of the first transmit routine below, first make sure that the transmit buffer isn't full. Then store the byte at the tail of the transmit buffer, point the tail to the next available location, and increment the transmit buffer counter (if used).
The 6551 transmit interrupt occurs when the transmit register is empty and available for transmitting another byte. Unless there are bytes to transmit, this creates unnecessary interrupts and wastes a lot of time. So, when the last byte is removed from the buffer, the interrupt handler in the first transmit routine below disables transmit interrupts.
Your program's code that stuffs new bytes into the transmit buffer must re-enable transmit interrupts, or your bytes may never be sent. A model for a main code routine for placing bytes into the transmit buffer follows the sample interrupt handler.
Using a transmit buffer allows your main program to perform other takes while the NMI interrupt routine takes care of sending bytes to the ACIA. If the buffer has more than a few characters, however, you may find that most of the processor time is spent servicing the interrupt. Since the ACIA generates NMI interrupts, you can't "mask" them from the processor, and you may have timing difficulties in your program.
One solution is to eliminate the transmit buffer completely. Your program can decide when to send each byte and perform any other necessary tasks in between bytes as needed. A model for the main-code routine for transmitting bytes without a transmit buffer is also shown following the sample interrupt-handler code. Feedback from developers to date is that many of them have better luck not using transmit interrupts or a transmit buffer.
Although it's possible to eliminate the receive buffer also, we strongly advise that you don't. The host computer, not your program, decides when a new byte will arrive. Polling the ACIA for received bytes instead of using an interrupt-driven buffer just waste's your program's time and risks missing data.
For a thorough discussion of the use of buffers, the Kernal RS-232 routines, and the Commodore NMI handler, see "COMPUTE!'s VIC-20 and Commodore 64 Tool Kit: Kernal", by Dan Heeb (COMPUTE! Books) and "What's Really Inside the Commodore 64", by Milton Bathurst (distributed in the US by Schnedler Systems).
Data received by the ACIA is placed in this register. If receive interrupts are enabled, an interrupt will be generated when all bits for a received byte have been assembled and the byte is ready to read.
Transmit interrupts, if enabled, are generated when this register is empty (available for transmitting). A byte to be transmitted can be placed in this register.
As the enclosed data sheet shows, the ACIA uses bits in this register to indicate data flow and errors.
If the ACIA generates an interrupt, bit #7 is set. There are four possible sources of interrupts:
Bit #5 indicates the status of the DSR line connected to the RS-232 device (modem, printer, etc.), while bit #6 indicates the status of the DCD line. NOTE: The function of these two bits is _reversed_ from the standard implementation. Unlike many ACIAs, the 6551 was designed to use the DCD (Data Carrier Detect) signal from the modem to activate the receiver section of the chip. If DCD is inactive (no carrier detected), the modem messages and echos of commands would not appear on the user's screen. We wanted the receiver active at all times. We also wanted to give the you access to the DCD signal from the modem. So, we exchanged the DCD and DSR signals at the ACIA. Both lines are pulled active internally by the cartridge if left unconnected by the user (i.e., in an null-modem cable possibility).
Bit #4 is set if the transmit register is empty. Your program must monitor this bit and not write to the data register until the bit i sset (see the sample XMIT code below).
Bit #3 is set if the receive register is full.
Bits #2, #1, and #0, when set, indicate overrun, framing, and parity errors in received bytes. The next data byte received erases the error information for the preceding byte. If you wish to use these bits, store them for processing by your program. The sample code below does not implement any error checking, but the Kernal software routines do, so adding features to your code might be a good idea.
You use bits #7, #6, and #5 to choose the parity checking desired.
Bit #4 should normally be cleared (i.e., no echo)
Bits #3 and #2 should reflect whether or not you are using transmit
interrupts, and if so, what kind. In the first sample transmit routine below, bit #3 is set and bit #2 is cleared to disable transmit interrupts (with RTS low [active]) on startup. However, when a byte is placed in the transmit buffer, bit #3 is cleared and bit #2 is set to enable transmit interrupts (with RTS low). When all bytes in the buffer have been transmitted, the interrupt handler disables transmit interrupts. NOTE: If you are connected to a RS-232 device that uses CTS/RTS handshaking, you can tell the device to stop temporarily by bringing RTS high (inactive): clear both bits #2 and #3.
Bit #1 should reflect whether or not you are using receive interrupts. In the sample code below, it is set to enable receive interrupts.
Bit #0 acts as a "master control switch" for all interrupts on the chip itself. It _must_ be set to enable any interrupts -- if it is cleared, all interrupts are turned off and the receiver section of the chip is disabled. This bit also pulls the DTR line low to enable communication with the connected RS-232 device. Clearing this bit causes most Hayes-compatible modems to hang up (by bringing DTR high). This bit should be cleared when a session is over and the user exits the terminal program to insure that no spurious interrupts are generated. One fairly elegant way to do this is to perform a software reset of the chip (writing any value to the Status register).
NOTE: In the figures on the 6551 data sheet, there are small charts at the bottom of each of the labelled "Hardware Reset/Program Reset". These charts indicate what values the bits of these registers contain after a hardware reset (like toggling the computer's power) and a program reset (a write to the Status register).
Be sure that bit #4, the "clock source" bit, is always set to use the on-chip crystal-controlled baud-rate generator.
You use the other bits to choose the baud rate, word length, and number of stop bits. Note that our cartridge uses a double-speed crystal, so values given on the data sheet are doubled [this is how they are shown below] (the minimum speed is 100 bps and the maximum speed is 38,400 bps).
Eight of the nine lines from the AT serial port are implemented: TxD, RxD, DTR, DSR, RTS, CTS, DCD, & GND. RI (Ring Indicator) is not implemented because the 6551 does not have a pin to handle it. CTS and RTS are not normally used by 2400 bps or slower Hayes-compatible modems, but these lines are being used by several newer, faster modems (MNP modems in particular). Note that although CTS is connected to the 6551, there is no way to monitor what state it is -- the value does not appear in any register. The 6551 handles CTS automatically: if it is pulled high (inactive) by the connected RS-232 device, the 6551 stops transmitting (clears the "transmit data register empty" bit [#4] in the status register).
The output signals are standard RS-232 level compatible. We've tested units with several commercial modems and with various computers using null-modem cables up to 38,400 bps without difficulties. In addition, there are pull-up resistors on three of the four input lines (DCD, DSR, CTS) so that if these pins are not connected in a cable, those three lines will pull to the active state. For example, if you happen to use a cable that is missing the DCD line, the pull-up resistor will pull the line active, so that bit #6 in the status register would be cleared (DCD is active low).
An on-board crystal provides the baud rate clock signal, with a maximum of 38.4 Kbaud, because we are using a double-speed crystal. If possible, test your program at 38.4 Kbaud as well as lower baud rates. Users may find this helpful for local file transfers using the C-64/C-128 as a dumb terminal on larger systems. And, after all, low-cost 28.8 Kb modems for the masses are just around the corner.
Default decoding for the ACIA addresses is done by the I/O #1 line (pin 7) on the cartridge port. This line is infrequently used on either the C-64 or C-128 and should allow compatibility with most other cartridge products, including the REU. The circuit board also has pads for users with special needs to change the decoding to I/O #2 (pin 10). This change moves the ACIA base address to $DF00, making it incompatible with the REU.
C-128 users may also elect to decode the ACIA at $D700 (this is a SID-chip mirror on the C-64). Since a $D700 decoding line is not available at the expansion port, the user would need to run a clip lead into the computer and connect to pin 12 of U2 (a 74LS138). We have tried this and it works. $D700 is an especially attractive location for C-128 BBS authors, because putting the SwiftLink there will free up the other two memory slots for devices that many BBS sysops use: IEEE and hard-drive interfaces.
Although we anticipate relatively few people changing ACIA decoding, you should allow your software to work with a SwiftLink at any of the three locations. You could either (1) test for the ACIA automatically by writing a value to the control register and then attempting to read it back or (2) provide a user-configurable switch/poke/menu option.
The Z80 CPU used for CP/M mode in the C-128 is not connected to the NMI line, which poses a problem since the cleanest software interface for C-64/C-128- mode programming is with this interrupt. We have added a switch to allow the ACIA interrupt to be connected to either NMI or IRQ, which the Z80 does use. The user can move this switch without opening the cartridge.
LOAD"SAMPLE",8,1 SYS8192It is a very simple terminal program. Press the STOP key to exit from it.
FEATURES:
MXS 6551 ___ - | | +---- Frequency range | Plain = 1 MHz | A = 2 MHz | B = 3 MHz | +----------- Package Designator C = Ceramic P = Plastic6551 PIN CONFIGURATION
+---------------+ GND --| 1 28 |-- R-/W CS0 --| 2 27 |-- o2 /CS1 --| 3 26 |-- /IRQ /RES --| 4 25 |-- DB7 RxC --| 5 24 |-- DB6 XTAL1 --| 6 23 |-- DB5 XTAL2 --| 7 22 |-- DB4 /RTS --| 8 21 |-- DB3 /CTS --| 9 20 |-- DB2 TxD --| 10 19 |-- DB1 /DTR --| 11 18 |-- DB0 RxD --| 12 17 |-- /DSR RS0 --| 13 16 |-- /DCD RS1 --| 14 15 |-- Vcc +---------------+BLOCK DIAGRAM
<not included here>
ELECTRICAL CHARACTERISTICS
<not included here>
POWER DISSIPATION vs TEMPERATURE
<not included here>
TIMING CHARACTERISTICS
<not included here>
INTERFACE SIGNAL DESCRIPTION
/RES (Reset)
During system initialization a low on the /RES input will cause internal
registers to be cleared.
o2 (Input Clock)
The input clock is the system o2 clock and is used to trigger all data
transfers between the system microprocessor and the 6551.
R-/W (Read/Write)
The R-/W is generated by the microprocessor and is used to control the
direction of data transfers. A high on the R-/W pin allows the processor
to read the data supplied by the 6551. A low on the R-/W pin allows a write
to the 6551.
/IRQ (Interrupt Request)
The /IRQ pin is an interrupt signal from the interrupt-control logic. It is
an open drain output, permitting several devices to be connected to the common
/IRQ microprocessor input. Normally a high level, /IRQ goes low when an
interrupt occurs.
DB0--DB7 (Data Bus)
The DB0--DB7 pins are the eight data lines used for transfer of data between
the processor and the 6551. These lines are bi-directional and are normally
high-impedance except during Read cycles when selected.
CS0, /CS1 (Chip Selects)
The two chip-select inputs are normally connected to the processor-address
lines either directly or through decoders. The 6551 is selected when CS0 is
high and /CS1 is low.
RS0, RS1 (Register Selects)
The two register-select lines are normally connected to the processor-address
lines to allow the processor to select the various 6551 internal registers.
The following table indicates the internal register-select coding:
ACIA/MODEM INTERFACE SIGNAL DESCRIPTION
XTAL1, XTAL2 (Crystal Pins)
These pins are normally directly connected to the external crystal (1.8432
MHz) used to derive the various baud rates. Alternatively, an externally
generated clock may be used to drive the XTAL1 pin, in which case the XTAL2
pin must float. XTAL1 is the input pin for the transmit clock.
TxD (Transmit Data)
The TxD output line is used to transfer serial NRZ (non-return-to-zero) data
to the modem. The LSB (least-significant bit) of the Transmit Data Register
is the first data bit transmitted and the reate of data transmission is
determined by the baud rate selected.
RxD (Receive Data)
The RxD input line is used to transfer serial NRZ data into the ACIA from the
modem, LSB first. The receiver data rate is either the programmed baud rate
or the rate of an externally generated receiver clock. This selection is made
by programming the Control Register.
RxC (Receive Clock)
The RxC is a bi-directional pin which serves as either the receiver 16x clock
input or the receiver 16x clock output. The latter mode results if the
internal baud rate generator is selected for receiver data clocking.
/RTS (Request to Send)
The /RTS output pin is used to control the modem from the processor. The
state of the /RTS pin is determined by the contents of the Command Register.
/CTS (Clear to Send)
The /CTS input pin is used to control the transmitter operation. The enable
state is with /CTS low. The transmitter is automatically disabled if /CTS is
high.
/DTR (Data Terminal Ready)
The output pin is used to indicate the status of the 6551 to the modem. A low
of /DTR indicates the 6551 is enabled and a high indicates it is disabled.
The processor controls this pin via bit 0 of the Command Register.
/DSR (Data Set Ready)
The /DSR input pin is used to indicate to the 6551 the status of the modem. A
low indicates the "ready" state and a high, "not-ready". /DSR is a high-
impedance input and must not be a no-connect. If unused, it should be driven
high or low, but not switched.
Note: If Command Register Bit #0 = 1 and a change of state on /DSR occurs,
/IRQ will be set and Status Register Bit #[5] will reflect the new level. The
state of /DSR does not affect Transmitter operation [but must be low for the
Receiver to operate]. [This statement reflects the SwiftLink implementation].
/DCD (Data Carrier Detect)
The /DCD input pin is used to indicate to the 6551 the status of the carrier-
detect output of the modem. A low indicates that the modem carrier signal is
present and a high, that it is not. /DCD, like /DSR, is a high-impedance
input and must not be a no-connect.
Note: If Command Register Bit #0 = 1 and a change of state on /DSR occurs,
/IRQ will be set and Status Register Bit #[6] will reflect the new level. The
state of /DCD does not affect either Transmitter or Receiver operation.
INTERNAL ORGANIZATION
<not included here>
TRANSMIT AND RECEIVE DATA REGISTERS (SL-Addr: $DE00 / 56832)
These registers are used as temporary data storage for the 6551 Transmit and
Receive circuits. The Transmit Data Register is characterized as follows:
The Status Register is used to indicate to the processor the status of various
6551 functions and is outlined here:
The Command Register is used to control specific Transmit/Receive functions
and is shown here:
The Control Register is used to select the desired mode for the 6551. The
word length, number of stop bits, and clock controls are all determined
by the Control Register, which is shown here:
RS1 RS0 WRITE READ SL-Addr
--- --- ---------------------- --------------------- -------
0 0 Transmit Data Register Receive Data Register $DE00
0 1 Programmed Reset* Status Register $DE01
1 0 Command Register Command Register $DE02
1 1 Control Register Control Register $DE03
* for programmed reset, data is "don't care".
The table shows that only the Command and Control registers are read/write.
The Programmed Reset operation does not cause any data transfer, but is used
to clear the 6551 registers. The Programmed Reset is slightly different from
the Hardware Reset (/RES) and these differences are described in the
individual register definitions.
The Receive Data Register is characterized in a similar fashion:
Transmit / Receive Data Register
+-----+-----+-----+-----+-----+-----+-----+-----+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| data |
The following figure illustrates a single transmitted or received data
word, for the example of 8 data bits, parity, and 1 stop bit:
"MARK"____
________________________________________________________"MARK"
| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | P | S .
|____|____|____|____|____|____|____|____|____|____|____|
start parity stop
bit ...data bits... bit bit
STATUS REGISTER (SL-Addr: $DE01 / 56833)
Command Register
+-----+-----+-----+-----+-----+-----+-----+-----+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| irq | dcd | dsr | txr | rxr | ovr | fe | pe |
+---+
| 7 | /IRQ*** : cleared by reading status register
+---+ --------------------------------------------
0 No interrupt
1 Interrupt
+---+
| 6 | /DCD : non-resetable, indicates /DCD status
+---+ --------------------------------------------
0 /DCD low
1 /DCD high
+---+
| 5 | /DSR : non-resetable, indicates /DSR status
+---+ --------------------------------------------
0 /DSR low
1 /DSR high
+---+
| 4 | Transmit Data Register Empty: Cleared by write to Tx Data reg
+---+ -------------------------------------------------------------
0 Not empty
1 Empty
+---+
| 3 | Receive Data Register Full: Cleared by read from Rx Data reg
+---+ -------------------------------------------------------------
0 Not full
1 Full
+---+
| 2 | Overrun*: Self-clearing**
+---+ -------------------------
0 No error
1 Error
+---+
| 1 | Framing Error*: Self-clearing**
+---+ -------------------------------
0 No error
1 Error
+---+
| 0 | Parity Error*: Self-clearing**
+---+ ------------------------------
0 No error
1 Error
Notes: * No interrupt generated for these conditions
** Cleared automatically after a read of RDR and the next error-
free receipt of data
*** Reading status reg. will clear the /IRQ bit except when
transmit intr. enabled
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| 0 | x | x | 1 | 0 | 0 | 0 | 0 | After Hardware reset
+---+---+---+---+---+---+---+---+
| x | x | x | x | x | 0 | x | x | After Software reset
+---+---+---+---+---+---+---+---+
COMMAND REGISTER (SL-Addr: $DE02 / 56834)
Command Register
+-----+-----+-----+-----+-----+-----+-----+-----+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
| parity | echo| tx ctrl | rxi | dtr |
+---+---+---+
| 7 | 6 | 5 | PARITY CHECK CONTROLS
+---+---+---+ ----------------------
x x 0 parity disabled--no parity bit generated or received
0 0 1 odd parity receiver and transmitter
0 1 1 even parity receiver and transmitter
1 0 1 mark parity transmitted, parity check disabled
1 1 1 space parity transmitted, parity check disabled
+---+
| 4 | NORMAL/ECHO MODE FOR RECEIVER
+---+ ------------------------------
0 Normal
1 Echo (bits 2 and 3 must be "0")
+---+---+
| 3 | 2 | Tx INTERRUPT RTS LEVEL TRANSMITTER
+---+---+ ------------ --------- ------------
0 0 Disabled High Off
0 1 Enabled Low On
1 0 Disabled Low On
1 1 Disabled Low Transmit BRK
+---+
| 1 | RECEIVE INTERRUPT ENABLE
+---+ -------------------------
0 /IRQ interrupt Enabled from bit 3 of Status Register
1 /IRQ interrupt Disabled
+---+
| 0 | DATA TERMINAL READY
+---+ --------------------
0 Disable Receiver and all interrupts (/DTR high)
1 Enable Receiver and all interrupts (/DTR low)
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | After Hardware reset
+---+---+---+---+---+---+---+---+
| x | x | x | 0 | 0 | 0 | 0 | 0 | After Software reset
+---+---+---+---+---+---+---+---+
CONTROL REGISTER (SL-Addr: $DE03 / 56835 / cpm: 0001xxxx)
Control Register
+-----+-----+-----+-----+-----+-----+-----+-----+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----+-----+-----+-----+-----+-----+-----+-----+
|stops| word len | src | baud rate |
+---+
| 7 | STOP BITS
+---+ ----------
0 1 stop bit
1 2 stop bits
1 1 stop bit if word length== 8 bits and parity
this allows for 9-bit transmission (8 data bits plus parity)
1 1.5 stop bits if word length== 5 bits and no parity
+---+---+
| 6 | 5 | WORD LENGTH
+---+---+ ------------
0 0 8 bits
0 1 7 bits
1 0 6 bits
1 1 5 bits
+---+
| 4 | RECEIVER CLOCK SOURCE
+---+ ----------
0 external receiver clock
1 baud rate generator
+---+---+---+---+
| 3 | 2 | 1 | 0 | BAUD RATE GENERATOR
+---+---+---+---+ --------------------
0 0 0 0 16x external clock
0 0 0 1 100 baud
0 0 1 0 150 baud
0 0 1 1 219.84 baud
0 1 0 0 269.16 baud
0 1 0 1 300 baud
0 1 1 0 600 baud
0 1 1 1 1200 baud
1 0 0 0 2400 baud
1 0 0 1 3600 baud
1 0 1 0 4800 baud
1 0 1 1 7200 baud
1 1 0 0 9600 baud
1 1 0 1 14400 baud
1 1 1 0 19200 baud
1 1 1 1 38400 baud
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | After Hardware reset
+---+---+---+---+---+---+---+---+
| x | x | x | x | x | x | x | x | After Software reset
+---+---+---+---+---+---+---+---+