summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/6551acia.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/6551acia.c')
-rw-r--r--trunk/src/emu/machine/6551acia.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/6551acia.c b/trunk/src/emu/machine/6551acia.c
new file mode 100644
index 00000000000..ce0d59baf68
--- /dev/null
+++ b/trunk/src/emu/machine/6551acia.c
@@ -0,0 +1,506 @@
+/*********************************************************************
+
+ 6551.h
+
+ MOS Technology 6551 Asynchronous Communications Interface Adapter
+
+ A1 A0 Write Read
+ 0 0 Transmit Data Register Receiver Data Register
+ 0 1 Programmed Reset Status Register
+ 1 0 Command Register
+ 1 1 Control Register
+
+*********************************************************************/
+
+#include "emu.h"
+#include "6551acia.h"
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type ACIA6551 = &device_creator<acia6551_device>;
+
+//-------------------------------------------------
+// acia6551_device - constructor
+//-------------------------------------------------
+
+acia6551_device::acia6551_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, ACIA6551, "MOS Technology 6551 ACIA", tag, owner, clock),
+ device_serial_interface(mconfig, *this)
+{
+}
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ input_callback - called when other side
+ has updated state
+-------------------------------------------------*/
+void acia6551_device::input_callback(UINT8 state)
+{
+ m_input_state = state;
+}
+
+/*-------------------------------------------------
+ timer_callback
+-------------------------------------------------*/
+void acia6551_device::timer_callback()
+{
+ /* get bit received from other side and update receive register */
+ receive_register_update_bit(get_in_data_bit());
+
+ if (is_receive_register_full())
+ {
+ receive_register_extract();
+ receive_character(get_received_char());
+ }
+
+ /* transmit register full? */
+ if ((m_status_register & (1<<4))==0)
+ {
+ /* if transmit reg is empty */
+ if (is_transmit_register_empty())
+ {
+ /* set it up */
+ transmit_register_setup(m_transmit_data_register);
+ /* acia transmit reg now empty */
+ m_status_register |=(1<<4);
+ /* and refresh ints */
+ refresh_ints();
+ }
+ }
+
+ /* if transmit is not empty... transmit data */
+ if (!is_transmit_register_empty())
+ {
+ // logerror("UART6551\n");
+ transmit_register_send_bit();
+ }
+}
+
+static TIMER_CALLBACK(acia_6551_timer_callback)
+{
+ reinterpret_cast<acia6551_device *>(ptr)->timer_callback();
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void acia6551_device::device_start()
+{
+ /* transmit data reg is empty */
+ m_status_register = (1<<4);
+ m_timer = machine().scheduler().timer_alloc(FUNC(acia_6551_timer_callback), (void *) this);
+
+ transmit_register_reset();
+ receive_register_reset();
+}
+
+
+
+/*-------------------------------------------------
+ refresh_ints - update interrupt output
+-------------------------------------------------*/
+
+void acia6551_device::refresh_ints()
+{
+ int interrupt_state;
+
+ interrupt_state = 0;
+
+ /* receive interrupts */
+
+ /* receive data register full? */
+ if (m_status_register & (1<<3))
+ {
+ /* receiver interrupt enable? */
+ if ((m_command_register & (1<<1))==0)
+ {
+ /* trigger a interrupt */
+ interrupt_state = 1;
+ }
+ }
+
+ /* set state of irq bit in status register */
+ m_status_register &= ~(1<<7);
+
+ if (interrupt_state)
+ {
+ m_status_register |=(1<<7);
+ }
+
+ //if (m_irq_callback != NULL)
+ //(*m_irq_callback)(interrupt_state);
+}
+
+
+
+/*-------------------------------------------------
+ receive_character
+-------------------------------------------------*/
+
+void acia6551_device::receive_character(UINT8 ch)
+{
+ /* receive register full? */
+ if (m_status_register & (1<<3))
+ {
+ /* set overrun error */
+ m_status_register |= (1<<2);
+ return;
+ }
+
+ /* set new byte */
+ m_receive_data_register = ch;
+ /* receive register is full */
+ m_status_register |= (1<<3);
+ /* update ints */
+ refresh_ints();
+}
+
+
+
+/*-------------------------------------------------
+ read
+-------------------------------------------------*/
+
+READ8_MEMBER(acia6551_device::read)
+{
+ unsigned char data;
+
+ data = 0x0ff;
+ switch (offset & 0x03)
+ {
+ case 0:
+ {
+ /* clear parity error, framing error and overrun error */
+ /* when read of data reigster is done */
+ m_status_register &=~((1<<0) | (1<<1) | (1<<2));
+ /* clear receive data register full flag */
+ m_status_register &=~(1<<3);
+ /* return data */
+ data = m_receive_data_register;
+ }
+ break;
+
+ /*
+ Status cleared by
+ b0 Parity error * (1: error) self clearing **
+ b1 Framing error * (1: error) self clearing **
+ b2 Overrun * (1: error) self clearing **
+ b3 Receive Data Register Full (1: full) Read Receive Data Register
+ b4 Transmit Data Reg Empty (1: empty) Write Transmit Data Register
+ b5 DCD (0: DCD low, 1: DCD high) Not resettable, reflects DCD state
+ b6 DSR (0: DSR low, 1: DCD high) Not resettable, reflects DSR state
+ b7 IRQ (0: no int., 1: interrupt) Read Status Register
+ */
+ case 1:
+ {
+ data = m_status_register;
+ /* clear interrupt */
+ m_status_register &= ~(1<<7);
+ }
+ break;
+
+ case 2:
+ data = m_command_register;
+ break;
+
+ case 3:
+ data = m_control_register;
+ break;
+
+ default:
+ break;
+ }
+
+ logerror("6551 R %04x %02x\n",offset & 0x03,data);
+
+ return data;
+}
+
+
+
+/*-------------------------------------------------
+ update_data_form
+-------------------------------------------------*/
+
+void acia6551_device::update_data_form()
+{
+ int word_length = 8-((m_control_register>>5) & 0x03);
+ int stop_bit_count = (m_control_register>>7)+1;
+ int parity = 0;
+ if (m_command_register & (1<<5))
+ {
+ parity = SERIAL_PARITY_ODD;
+ }
+ else
+ {
+ parity = SERIAL_PARITY_NONE;
+ }
+
+ set_data_frame(word_length, stop_bit_count, parity);
+}
+
+
+
+/*-------------------------------------------------
+ write
+-------------------------------------------------*/
+
+WRITE8_MEMBER(acia6551_device::write)
+{
+ logerror("6551 W %04x %02x\n",offset & 0x03, data);
+
+ switch (offset & 0x03)
+ {
+ case 0:
+ {
+ /* clear transmit data register empty */
+ m_status_register &= ~(1<<4);
+ /* store byte */
+ m_transmit_data_register = data;
+
+ }
+ break;
+
+ case 1:
+ {
+ /* telstrat writes 0x07f! */
+ }
+ break;
+
+
+ /*
+ Command Register:
+
+ b0 Data Terminal Ready
+ 0 : disable receiver and all interrupts (DTR high)
+ 1 : enable receiver and all interrupts (DTR low)
+ b1 Receiver Interrupt Enable
+ 0 : IRQ interrupt enabled from bit 3 of status register
+ 1 : IRQ interrupt disabled
+ b3,b2 Transmitter Control
+ Transmit Interrupt RTS level Transmitter
+ 00 disabled high off
+ 01 enabled low on
+ 10 disabled low on
+ 11 disabled low Transmit BRK
+ b4 Normal/Echo Mode for Receiver
+ 0 : normal
+ 1 : echo (bits 2 and 3 must be 0)
+ b5 Parity Enable
+ 0 : parity disabled, no parity bit generated or received
+ 1 : parity enabled
+ b7,b6 Parity
+ 00 : odd parity receiver and transmitter
+ 01 : even parity receiver and transmitter
+ 10 : mark parity bit transmitted, parity check disabled
+ 11 : space parity bit transmitted, parity check disabled
+ */
+
+ case 2:
+ {
+ m_command_register = data;
+
+ /* update state of dtr */
+ m_connection_state &=~SERIAL_STATE_DTR;
+ if (m_command_register & (1<<0))
+ {
+ m_connection_state |=SERIAL_STATE_DTR;
+ }
+
+ /* update state of rts */
+ switch ((m_command_register>>2) & 0x03)
+ {
+ case 0:
+ {
+ m_connection_state &=~SERIAL_STATE_RTS;
+ }
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ {
+ m_connection_state |=SERIAL_STATE_RTS;
+ }
+ break;
+ }
+
+ serial_connection_out();
+ update_data_form();
+ }
+ break;
+
+ /*
+ Control register:
+
+ b3-b0 baud rate generator:
+ 0000 : 16x external clock
+ 0001 : 50 baud
+ 0010 : 75 25
+ 0011 : 110 35
+ 0100 : 134.5
+ 0101 : 150
+ 0110 : 300 150
+ 0111 : 600 300
+ 1000 : 1200 600
+ 1001 : 1800 600
+ 1010 : 2400 600
+ 1011 : 3600 1200
+ 1100 : 4800 1200
+ 1101 : 7200 2400
+ 1110 : 9600 2400
+ 1111 : 19,200 9600
+ b4 receiver clock source
+ 0 : external receiver clock
+ 1 : baud rate generator
+ b6,b5 word length
+ 00 : 8 bits
+ 01 : 7
+ 10 : 6
+ 11 : 5
+ b7 stop bits
+ 0 : 1 stop bit
+ 1 : 2 stop bits
+ (1 stop bit if parity and word length = 8)
+ (1 1/2 stop bits if word length = 5 and no parity)
+ */
+ case 3:
+ {
+ unsigned char previous_control_register;
+
+ previous_control_register = m_control_register;
+
+ if (((previous_control_register^data) & 0x07)!=0)
+ {
+ int rate;
+
+ rate = data & 0x07;
+
+ /* baud rate changed? */
+ m_timer->reset();
+
+ if (rate==0)
+ {
+ /* 16x external clock */
+ logerror("6551: external clock not supported!\n");
+ }
+ else
+ {
+ int baud_rate;
+
+ switch (rate)
+ {
+
+ default:
+ case 1:
+ {
+ baud_rate = 50;
+ }
+ break;
+
+ case 2:
+ {
+ baud_rate = 75;
+ }
+ break;
+
+ case 3:
+ {
+ baud_rate = 110;
+ }
+ break;
+
+ case 4:
+ {
+ baud_rate = 135;
+ }
+ break;
+
+ case 5:
+ {
+ baud_rate = 150;
+ }
+ break;
+
+ case 6:
+ {
+ baud_rate = 300;
+ }
+ break;
+
+ case 7:
+ {
+ baud_rate = 600;
+ }
+ break;
+
+ case 8:
+ {
+ baud_rate = 1200;
+ }
+ break;
+
+ case 9:
+ {
+ baud_rate = 1800;
+ }
+ break;
+
+ case 10:
+ {
+ baud_rate = 2400;
+ }
+ break;
+
+ case 11:
+ {
+ baud_rate = 3600;
+ }
+ break;
+
+ case 12:
+ {
+ baud_rate = 4800;
+ }
+ break;
+
+ case 13:
+ {
+ baud_rate = 7200;
+ }
+ break;
+
+ case 14:
+ {
+ baud_rate = 9600;
+ }
+ break;
+
+ case 15:
+ {
+ baud_rate = 19200;
+ }
+ break;
+ }
+
+ m_timer->adjust(attotime::zero, 0, attotime::from_hz(baud_rate));
+ }
+ }
+
+ m_control_register = data;
+ update_data_form();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}