diff options
Diffstat (limited to 'trunk/src/emu/machine/mc68901.c')
-rw-r--r-- | trunk/src/emu/machine/mc68901.c | 1476 |
1 files changed, 1476 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/mc68901.c b/trunk/src/emu/machine/mc68901.c new file mode 100644 index 00000000000..186b705e25b --- /dev/null +++ b/trunk/src/emu/machine/mc68901.c @@ -0,0 +1,1476 @@ +/********************************************************************** + + Motorola MC68901 Multi Function Peripheral emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +/* + + TODO: + + - daisy chaining + - disable GPIO3/4 interrupts when timer A/B in pulse mode + - spurious interrupt + + If you look at the MFP datasheet it is obvious that it can generate the conditions for a spurious interrupt. + However the fact that they indeed happen in the ST is quite interesting. + + The MFP will generate a spurious interrupt if interrupts are disabled (by changing the IERA/IERB registers) + at the ???precise point???. The precise point would be after the system (but not necessarily the CPU, see below) + triggered an MFP interrupt, and before the CPU drives the interrupt acknowledge cycle. + + If the MFP was connected directly to the CPU, spurious interrupts probably couldn???t happen. However in the + ST, GLUE seats in the middle and handles all the interrupt timing. It is possible that GLUE introduces a + delay between detecting a change in the MFP interrupt request signal and actually propagating the change to + the CPU IPL signals (it is even possible that GLUE make some kind of latching). This would create a window + long enough for the ???precise point??? described above. + + "yes, the spurious interrupt occurs when i mask a timer. i did not notice an occurance of the SPI when changing data and control registers. + if i kill interrupts with the status reg before masking the timer interrupt, then the SPI occurs as soon as the status register is set to re-enable interrupts." + + - divide serial clock by 16 + - synchronous mode + - 1.5/2 stop bits + - interrupt on receiver break end + - interrupt on character boundaries during break transmission + - loopback mode + +*/ + +#include "emu.h" +#include "mc68901.h" +#include "cpu/m68000/m68000.h" +#include "machine/devhelpr.h" + + +// device type definition +const device_type MC68901 = &device_creator<mc68901_device>; + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 0 + + +enum +{ + REGISTER_GPIP = 0, + REGISTER_AER, + REGISTER_DDR, + REGISTER_IERA, + REGISTER_IERB, + REGISTER_IPRA, + REGISTER_IPRB, + REGISTER_ISRA, + REGISTER_ISRB, + REGISTER_IMRA, + REGISTER_IMRB, + REGISTER_VR, + REGISTER_TACR, + REGISTER_TBCR, + REGISTER_TCDCR, + REGISTER_TADR, + REGISTER_TBDR, + REGISTER_TCDR, + REGISTER_TDDR, + REGISTER_SCR, + REGISTER_UCR, + REGISTER_RSR, + REGISTER_TSR, + REGISTER_UDR +}; + + +enum +{ + INT_GPI0 = 0, + INT_GPI1, + INT_GPI2, + INT_GPI3, + INT_TIMER_D, + INT_TIMER_C, + INT_GPI4, + INT_GPI5, + INT_TIMER_B, + INT_XMIT_ERROR, + INT_XMIT_BUFFER_EMPTY, + INT_RCV_ERROR, + INT_RCV_BUFFER_FULL, + INT_TIMER_A, + INT_GPI6, + INT_GPI7 +}; + + +enum +{ + GPIP_0 = 0, + GPIP_1, + GPIP_2, + GPIP_3, + GPIP_4, + GPIP_5, + GPIP_6, + GPIP_7 +}; + + +enum +{ + TIMER_A = 0, + TIMER_B, + TIMER_C, + TIMER_D, + MAX_TIMERS +}; + + +enum +{ + SERIAL_START = 0, + SERIAL_DATA, + SERIAL_PARITY, + SERIAL_STOP +}; + + +enum +{ + XMIT_OFF = 0, + XMIT_STARTING, + XMIT_ON, + XMIT_BREAK, + XMIT_STOPPING +}; + + +#define AER_GPIP_0 0x01 +#define AER_GPIP_1 0x02 +#define AER_GPIP_2 0x04 +#define AER_GPIP_3 0x08 +#define AER_GPIP_4 0x10 +#define AER_GPIP_5 0x20 +#define AER_GPIP_6 0x40 +#define AER_GPIP_7 0x80 + + +#define VR_S 0x08 + + +#define IR_GPIP_0 0x0001 +#define IR_GPIP_1 0x0002 +#define IR_GPIP_2 0x0004 +#define IR_GPIP_3 0x0008 +#define IR_TIMER_D 0x0010 +#define IR_TIMER_C 0x0020 +#define IR_GPIP_4 0x0040 +#define IR_GPIP_5 0x0080 +#define IR_TIMER_B 0x0100 +#define IR_XMIT_ERROR 0x0200 +#define IR_XMIT_BUFFER_EMPTY 0x0400 +#define IR_RCV_ERROR 0x0800 +#define IR_RCV_BUFFER_FULL 0x1000 +#define IR_TIMER_A 0x2000 +#define IR_GPIP_6 0x4000 +#define IR_GPIP_7 0x8000 + + +#define TCR_TIMER_STOPPED 0x00 +#define TCR_TIMER_DELAY_4 0x01 +#define TCR_TIMER_DELAY_10 0x02 +#define TCR_TIMER_DELAY_16 0x03 +#define TCR_TIMER_DELAY_50 0x04 +#define TCR_TIMER_DELAY_64 0x05 +#define TCR_TIMER_DELAY_100 0x06 +#define TCR_TIMER_DELAY_200 0x07 +#define TCR_TIMER_EVENT 0x08 +#define TCR_TIMER_PULSE_4 0x09 +#define TCR_TIMER_PULSE_10 0x0a +#define TCR_TIMER_PULSE_16 0x0b +#define TCR_TIMER_PULSE_50 0x0c +#define TCR_TIMER_PULSE_64 0x0d +#define TCR_TIMER_PULSE_100 0x0e +#define TCR_TIMER_PULSE_200 0x0f +#define TCR_TIMER_RESET 0x10 + + +#define UCR_PARITY_ENABLED 0x04 +#define UCR_PARITY_EVEN 0x02 +#define UCR_PARITY_ODD 0x00 +#define UCR_WORD_LENGTH_8 0x00 +#define UCR_WORD_LENGTH_7 0x20 +#define UCR_WORD_LENGTH_6 0x40 +#define UCR_WORD_LENGTH_5 0x60 +#define UCR_START_STOP_0_0 0x00 +#define UCR_START_STOP_1_1 0x08 +#define UCR_START_STOP_1_15 0x10 +#define UCR_START_STOP_1_2 0x18 +#define UCR_CLOCK_DIVIDE_16 0x80 +#define UCR_CLOCK_DIVIDE_1 0x00 + + +#define RSR_RCV_ENABLE 0x01 +#define RSR_SYNC_STRIP_ENABLE 0x02 +#define RSR_MATCH 0x04 +#define RSR_CHAR_IN_PROGRESS 0x04 +#define RSR_FOUND_SEARCH 0x08 +#define RSR_BREAK 0x08 +#define RSR_FRAME_ERROR 0x10 +#define RSR_PARITY_ERROR 0x20 +#define RSR_OVERRUN_ERROR 0x40 +#define RSR_BUFFER_FULL 0x80 + +#define TSR_XMIT_ENABLE 0x01 +#define TSR_OUTPUT_HI_Z 0x00 +#define TSR_OUTPUT_LOW 0x02 +#define TSR_OUTPUT_HIGH 0x04 +#define TSR_OUTPUT_LOOP 0x06 +#define TSR_OUTPUT_MASK 0x06 +#define TSR_BREAK 0x08 +#define TSR_END_OF_XMIT 0x10 +#define TSR_AUTO_TURNAROUND 0x20 +#define TSR_UNDERRUN_ERROR 0x40 +#define TSR_BUFFER_EMPTY 0x80 + + +static const int INT_MASK_GPIO[] = +{ + IR_GPIP_0, IR_GPIP_1, IR_GPIP_2, IR_GPIP_3, + IR_GPIP_4, IR_GPIP_5, IR_GPIP_6, IR_GPIP_7 +}; + + +static const int INT_MASK_TIMER[] = +{ + IR_TIMER_A, IR_TIMER_B, IR_TIMER_C, IR_TIMER_D +}; + + +static const int GPIO_TIMER[] = +{ + GPIP_4, GPIP_3 +}; + + +static const int PRESCALER[] = { 0, 4, 10, 16, 50, 64, 100, 200 }; + + +#define TXD(_data) m_out_so_func(_data); + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +inline void mc68901_device::check_interrupts() +{ + if (m_ipr & m_imr) + { + m_out_irq_func(ASSERT_LINE); + } + else + { + m_out_irq_func(CLEAR_LINE); + } +} + +inline void mc68901_device::take_interrupt(UINT16 mask) +{ + m_ipr |= mask; + + check_interrupts(); +} + +inline void mc68901_device::rx_buffer_full() +{ + if (m_ier & IR_RCV_BUFFER_FULL) + { + take_interrupt(IR_RCV_BUFFER_FULL); + } +} + +inline void mc68901_device::rx_error() +{ + if (m_ier & IR_RCV_ERROR) + { + take_interrupt(IR_RCV_ERROR); + } + else + { + rx_buffer_full(); + } +} + +inline void mc68901_device::tx_buffer_empty() +{ + if (m_ier & IR_XMIT_BUFFER_EMPTY) + { + take_interrupt(IR_XMIT_BUFFER_EMPTY); + } +} + +inline void mc68901_device::tx_error() +{ + if (m_ier & IR_XMIT_ERROR) + { + take_interrupt(IR_XMIT_ERROR); + } + else + { + tx_buffer_empty(); + } +} + +inline int mc68901_device::get_parity_bit(UINT8 b) +{ + b ^= b >> 4; + b ^= b >> 2; + b ^= b >> 1; + return b & 1; +} + +inline void mc68901_device::serial_receive() +{ + int rxd; + + if (!(m_rsr & RSR_RCV_ENABLE)) return; + + rxd = m_in_si_func(); + + switch (m_rx_state) + { + case SERIAL_START: + if (!rxd) + { + m_rsr |= RSR_CHAR_IN_PROGRESS; + m_rx_bits = 0; + m_rx_buffer = 0; + m_rx_state = SERIAL_DATA; + m_next_rsr = RSR_BREAK; + } + break; + + case SERIAL_DATA: + if ((m_next_rsr & RSR_BREAK) && (rxd == 1) && m_rsr_read) + { + m_next_rsr &= ~RSR_BREAK; + } + + m_rx_buffer >>= 1; + m_rx_buffer |= (rxd << 7); + m_rx_bits++; + + if (m_rx_bits == m_rxtx_word) + { + if (m_rxtx_word < 8) + { + m_rx_buffer >>= (8 - m_rxtx_word); + } + + m_rsr &= ~RSR_CHAR_IN_PROGRESS; + + if (m_ucr & UCR_PARITY_ENABLED) + { + m_rx_state = SERIAL_PARITY; + } + else + { + m_rx_state = SERIAL_STOP; + } + } + break; + + case SERIAL_PARITY: + m_rx_parity = rxd; + + if (m_rx_parity != (get_parity_bit(m_rx_buffer) ^ ((m_ucr & UCR_PARITY_EVEN) >> 1))) + { + m_next_rsr |= RSR_PARITY_ERROR; + } + + m_rx_state = SERIAL_STOP; + break; + + case SERIAL_STOP: + if (rxd == 1) + { + if (!((m_rsr & RSR_SYNC_STRIP_ENABLE) && (m_rx_buffer == m_scr))) + { + if (!(m_rsr & RSR_OVERRUN_ERROR)) + { + if (m_rsr & RSR_BUFFER_FULL) + { + // incoming word received but last word in receive buffer has not been read + m_next_rsr |= RSR_OVERRUN_ERROR; + } + else + { + // incoming word received and receive buffer is empty + m_rsr |= RSR_BUFFER_FULL; + m_udr = m_rx_buffer; + rx_buffer_full(); + } + } + } + } + else + { + if (m_rx_buffer) + { + // non-zero data word not followed by a stop bit + m_next_rsr |= RSR_FRAME_ERROR; + } + } + + m_rx_state = SERIAL_START; + break; + } +} + + +inline void mc68901_device::tx_disabled() +{ + switch (m_tsr & TSR_OUTPUT_MASK) + { + case TSR_OUTPUT_HI_Z: + /* indeterminate state */ + case TSR_OUTPUT_LOW: + TXD(0); + break; + + case TSR_OUTPUT_HIGH: + case TSR_OUTPUT_LOOP: + TXD(1); + break; + } +} + + +inline void mc68901_device::tx_starting() +{ + if (m_tsr & TSR_XMIT_ENABLE) + { + /* enable transmitter */ + TXD(1); + m_xmit_state = XMIT_ON; + } + else + { + /* disable transmitter */ + m_tsr |= TSR_END_OF_XMIT; + m_xmit_state = XMIT_OFF; + } +} + + +inline void mc68901_device::tx_break() +{ + if (m_tsr & TSR_XMIT_ENABLE) + { + if (m_tsr & TSR_BREAK) + { + /* transmit break */ + TXD(1); + } + else + { + /* enable transmitter */ + m_xmit_state = XMIT_ON; + } + } + else + { + /* disable transmitter */ + m_tsr |= TSR_END_OF_XMIT; + m_xmit_state = XMIT_OFF; + } +} + + +inline void mc68901_device::tx_enabled() +{ + switch (m_tx_state) + { + case SERIAL_START: + if (m_tsr & TSR_UNDERRUN_ERROR) + { + /* buffer underrun condition */ + if (m_tsr & TSR_XMIT_ENABLE) + { + /* transmit break */ + TXD(1); + } + else + { + /* transmitter disabled */ + tx_disabled(); + } + } + else + { + if (m_tsr & TSR_BUFFER_EMPTY) + { + /* transmit buffer is empty, signal underrun error */ + m_tsr |= TSR_UNDERRUN_ERROR; + + if (m_tsr & TSR_XMIT_ENABLE) + { + /* transmit break */ + TXD(1); + } + else + { + /* transmitter disabled */ + tx_disabled(); + } + } + else + { + /* transmit start bit */ + TXD(0); + + /* load transmit buffer */ + m_tx_buffer = m_udr; + m_tx_bits = 0; + + /* signal transmit buffer empty */ + m_tsr |= TSR_BUFFER_EMPTY; + tx_buffer_empty(); + + /* calculate parity */ + m_tx_parity = get_parity_bit(m_tx_buffer); + + /* next bit is data */ + m_tx_state = SERIAL_DATA; + } + } + break; + + case SERIAL_DATA: + /* transmit data bit */ + TXD(m_tx_buffer & 0x01); + + /* shift transmit buffer */ + m_tx_buffer >>= 1; + m_tx_bits++; + + if (m_tx_bits == m_rxtx_word) + { + /* all data bits transferred */ + if (m_ucr & UCR_PARITY_ENABLED) + { + /* next bit is parity */ + m_tx_state = SERIAL_PARITY; + } + else + { + /* next bit is stop */ + m_tx_state = SERIAL_STOP; + } + } + break; + + case SERIAL_PARITY: + if (m_rxtx_word < 8) + { + /* transmit user-defined parity bit from buffer */ + TXD(m_tx_buffer & 0x01); + } + else + { + /* transmit calculated parity bit */ + TXD(m_tx_parity ^ ((m_ucr & UCR_PARITY_EVEN) >> 1)); + } + + /* next bit is stop */ + m_tx_state = SERIAL_STOP; + break; + + case SERIAL_STOP: + /* transmit stop bit */ + TXD(1); + + if (m_tsr & TSR_XMIT_ENABLE) + { + /* next bit is start */ + m_tx_state = SERIAL_START; + } + else + { + if (m_tsr & TSR_AUTO_TURNAROUND) + { + /* enable transmitter */ + m_tsr |= TSR_XMIT_ENABLE; + + /* next bit is start */ + m_tx_state = SERIAL_START; + } + else + { + /* disable transmitter */ + m_xmit_state = XMIT_OFF; + m_tsr |= TSR_END_OF_XMIT; + + /* signal transmit error */ + tx_error(); + } + } + break; + } +} + +inline void mc68901_device::serial_transmit() +{ + switch (m_xmit_state) + { + case XMIT_OFF: tx_disabled(); break; + case XMIT_STARTING: tx_starting(); break; + case XMIT_BREAK: tx_break(); break; + case XMIT_ON: tx_enabled(); break; + } +} + + +inline void mc68901_device::timer_count(int index) +{ + if (m_tmc[index] == 0x01) + { + /* toggle timer output signal */ + m_to[index] = !m_to[index]; + + switch (index) + { + case TIMER_A: m_out_tao_func(m_to[index]); break; + case TIMER_B: m_out_tbo_func(m_to[index]); break; + case TIMER_C: m_out_tco_func(m_to[index]); break; + case TIMER_D: m_out_tdo_func(m_to[index]); break; + } + + if (m_ier & INT_MASK_TIMER[index]) + { + /* signal timer elapsed interrupt */ + take_interrupt(INT_MASK_TIMER[index]); + } + + /* load main counter */ + m_tmc[index] = m_tdr[index]; + } + else + { + /* count down */ + m_tmc[index]--; + } +} + + +inline void mc68901_device::timer_input(int index, int value) +{ + int bit = GPIO_TIMER[index]; + int aer = BIT(m_aer, bit); + int cr = index ? m_tbcr : m_tacr; + + switch (cr & 0x0f) + { + case TCR_TIMER_EVENT: + if (((m_ti[index] ^ aer) == 1) && ((value ^ aer) == 0)) + { + timer_count(index); + } + + m_ti[index] = value; + break; + + case TCR_TIMER_PULSE_4: + case TCR_TIMER_PULSE_10: + case TCR_TIMER_PULSE_16: + case TCR_TIMER_PULSE_50: + case TCR_TIMER_PULSE_64: + case TCR_TIMER_PULSE_100: + case TCR_TIMER_PULSE_200: + m_timer[index]->enable((value == aer)); + + if (((m_ti[index] ^ aer) == 0) && ((value ^ aer) == 1)) + { + if (m_ier & INT_MASK_GPIO[bit]) + { + take_interrupt(INT_MASK_GPIO[bit]); + } + } + + m_ti[index] = value; + break; + } +} + + +inline void mc68901_device::gpio_input(int bit, int state) +{ + if (BIT(m_gpip, bit) && !state) // if transition from 1 to 0 is detected... + { + if (LOG) logerror("MC68901 '%s' Edge Transition Detected on GPIO%u\n", tag(), bit); + + if (m_ier & INT_MASK_GPIO[bit]) // AND interrupt enabled bit is set... + { + if (LOG) logerror("MC68901 '%s' Interrupt Pending for GPIO%u\n", tag(), bit); + + take_interrupt(INT_MASK_GPIO[bit]); // set interrupt pending bit + } + } + + m_gpip &= ((1 << bit) ^ 0xff); + m_gpip |= (state << bit); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// mc68901_device - constructor +//------------------------------------------------- + +mc68901_device::mc68901_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MC68901, "Motorola MC68901", tag, owner, clock), + m_gpip(0), + m_tsr(TSR_BUFFER_EMPTY) +{ +} + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc68901_device::device_config_complete() +{ + // inherit a copy of the static data + const mc68901_interface *intf = reinterpret_cast<const mc68901_interface *>(static_config()); + if (intf != NULL) + *static_cast<mc68901_interface *>(this) = *intf; + + // or initialize to defaults if none provided + else + { +// memset(&in_pa_cb, 0, sizeof(in_pa_cb)); + } +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc68901_device::device_start() +{ + /* resolve callbacks */ + m_in_gpio_func.resolve(m_in_gpio_cb, *this); + m_out_gpio_func.resolve(m_out_gpio_cb, *this); + m_in_si_func.resolve(m_in_si_cb, *this); + m_out_so_func.resolve(m_out_so_cb, *this); + m_out_tao_func.resolve(m_out_tao_cb, *this); + m_out_tbo_func.resolve(m_out_tbo_cb, *this); + m_out_tco_func.resolve(m_out_tco_cb, *this); + m_out_tdo_func.resolve(m_out_tdo_cb, *this); + m_out_irq_func.resolve(m_out_irq_cb, *this); + + /* create the timers */ + m_timer[TIMER_A] = timer_alloc(TIMER_A); + m_timer[TIMER_B] = timer_alloc(TIMER_B); + m_timer[TIMER_C] = timer_alloc(TIMER_C); + m_timer[TIMER_D] = timer_alloc(TIMER_D); + + if (m_rx_clock > 0) + { + m_rx_timer = timer_alloc(TIMER_RX); + m_rx_timer->adjust(attotime::zero, 0, attotime::from_hz(m_rx_clock)); + } + + if (m_tx_clock > 0) + { + m_tx_timer = timer_alloc(TIMER_TX); + m_tx_timer->adjust(attotime::zero, 0, attotime::from_hz(m_tx_clock)); + } + + /* register for state saving */ + save_item(NAME(m_gpip)); + save_item(NAME(m_aer)); + save_item(NAME(m_ddr)); + save_item(NAME(m_ier)); + save_item(NAME(m_ipr)); + save_item(NAME(m_isr)); + save_item(NAME(m_imr)); + save_item(NAME(m_vr)); + save_item(NAME(m_tacr)); + save_item(NAME(m_tbcr)); + save_item(NAME(m_tcdcr)); + save_item(NAME(m_tdr)); + save_item(NAME(m_tmc)); + save_item(NAME(m_to)); + save_item(NAME(m_ti)); + save_item(NAME(m_scr)); + save_item(NAME(m_ucr)); + save_item(NAME(m_rsr)); + save_item(NAME(m_tsr)); + save_item(NAME(m_udr)); + save_item(NAME(m_rx_bits)); + save_item(NAME(m_tx_bits)); + save_item(NAME(m_rx_parity)); + save_item(NAME(m_tx_parity)); + save_item(NAME(m_rx_state)); + save_item(NAME(m_tx_state)); + save_item(NAME(m_rx_buffer)); + save_item(NAME(m_tx_buffer)); + save_item(NAME(m_xmit_state)); + save_item(NAME(m_rxtx_word)); + save_item(NAME(m_rxtx_start)); + save_item(NAME(m_rxtx_stop)); + save_item(NAME(m_rsr_read)); + save_item(NAME(m_next_rsr)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc68901_device::device_reset() +{ + m_xmit_state = XMIT_OFF; + m_rx_state = SERIAL_STOP; + m_rx_buffer = 0; + m_tx_buffer = 0; + + // Avoid read-before-write + m_ipr = m_imr = 0; + + register_w(REGISTER_GPIP, 0); + register_w(REGISTER_AER, 0); + register_w(REGISTER_DDR, 0); + register_w(REGISTER_IERA, 0); + register_w(REGISTER_IERB, 0); + register_w(REGISTER_IPRA, 0); + register_w(REGISTER_IPRB, 0); + register_w(REGISTER_ISRA, 0); + register_w(REGISTER_ISRB, 0); + register_w(REGISTER_IMRA, 0); + register_w(REGISTER_IMRB, 0); + register_w(REGISTER_VR, 0); + register_w(REGISTER_TACR, 0); + register_w(REGISTER_TBCR, 0); + register_w(REGISTER_TCDCR, 0); + register_w(REGISTER_SCR, 0); + register_w(REGISTER_UCR, 0); + register_w(REGISTER_RSR, 0); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void mc68901_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_RX: + serial_receive(); + break; + + case TIMER_TX: + serial_transmit(); + break; + + default: + timer_count(id); + break; + } +} + + + +READ8_MEMBER( mc68901_device::read ) +{ + switch (offset) + { + case REGISTER_GPIP: + m_gpip = m_in_gpio_func(0); + return m_gpip; + + case REGISTER_AER: return m_aer; + case REGISTER_DDR: return m_ddr; + + case REGISTER_IERA: return m_ier >> 8; + case REGISTER_IERB: return m_ier & 0xff; + case REGISTER_IPRA: return m_ipr >> 8; + case REGISTER_IPRB: return m_ipr & 0xff; + case REGISTER_ISRA: return m_isr >> 8; + case REGISTER_ISRB: return m_isr & 0xff; + case REGISTER_IMRA: return m_imr >> 8; + case REGISTER_IMRB: return m_imr & 0xff; + case REGISTER_VR: return m_vr; + + case REGISTER_TACR: return m_tacr; + case REGISTER_TBCR: return m_tbcr; + case REGISTER_TCDCR: return m_tcdcr; + case REGISTER_TADR: return m_tmc[TIMER_A]; + case REGISTER_TBDR: return m_tmc[TIMER_B]; + case REGISTER_TCDR: return m_tmc[TIMER_C]; + case REGISTER_TDDR: return m_tmc[TIMER_D]; + + case REGISTER_SCR: return m_scr; + case REGISTER_UCR: return m_ucr; + case REGISTER_RSR: + m_rsr_read = 1; + return m_rsr; + + case REGISTER_TSR: + { + /* clear UE bit (in reality, this won't be cleared until one full clock cycle of the transmitter has passed since the bit was set) */ + UINT8 tsr = m_tsr; + m_tsr &= 0xbf; + + return tsr; + } + + case REGISTER_UDR: + /* load RSR with latched value */ + m_rsr = (m_next_rsr & 0x7c) | (m_rsr & 0x03); + m_next_rsr = 0; + + if (m_rsr & 0x78) + { + /* signal receiver error interrupt */ + rx_error(); + } + + return m_udr; + + default: return 0; + } +} + + +#define DIVISOR PRESCALER[data & 0x07] + +void mc68901_device::register_w(offs_t offset, UINT8 data) +{ + switch (offset) + { + case REGISTER_GPIP: + if (LOG) logerror("MC68901 '%s' General Purpose I/O : %x\n", tag(), data); + m_gpip = data & m_ddr; + + m_out_gpio_func(0, m_gpip); + break; + + case REGISTER_AER: + if (LOG) logerror("MC68901 '%s' Active Edge Register : %x\n", tag(), data); + m_aer = data; + break; + + case REGISTER_DDR: + if (LOG) logerror("MC68901 '%s' Data Direction Register : %x\n", tag(), data); + m_ddr = data; + break; + + case REGISTER_IERA: + if (LOG) logerror("MC68901 '%s' Interrupt Enable Register A : %x\n", tag(), data); + m_ier = (data << 8) | (m_ier & 0xff); + m_ipr &= m_ier; + check_interrupts(); + break; + + case REGISTER_IERB: + if (LOG) logerror("MC68901 '%s' Interrupt Enable Register B : %x\n", tag(), data); + m_ier = (m_ier & 0xff00) | data; + m_ipr &= m_ier; + check_interrupts(); + break; + + case REGISTER_IPRA: + if (LOG) logerror("MC68901 '%s' Interrupt Pending Register A : %x\n", tag(), data); + m_ipr &= (data << 8) | (m_ipr & 0xff); + check_interrupts(); + break; + + case REGISTER_IPRB: + if (LOG) logerror("MC68901 '%s' Interrupt Pending Register B : %x\n", tag(), data); + m_ipr &= (m_ipr & 0xff00) | data; + check_interrupts(); + break; + + case REGISTER_ISRA: + if (LOG) logerror("MC68901 '%s' Interrupt In-Service Register A : %x\n", tag(), data); + m_isr &= (data << 8) | (m_isr & 0xff); + break; + + case REGISTER_ISRB: + if (LOG) logerror("MC68901 '%s' Interrupt In-Service Register B : %x\n", tag(), data); + m_isr &= (m_isr & 0xff00) | data; + break; + + case REGISTER_IMRA: + if (LOG) logerror("MC68901 '%s' Interrupt Mask Register A : %x\n", tag(), data); + m_imr = (data << 8) | (m_imr & 0xff); + m_isr &= m_imr; + check_interrupts(); + break; + + case REGISTER_IMRB: + if (LOG) logerror("MC68901 '%s' Interrupt Mask Register B : %x\n", tag(), data); + m_imr = (m_imr & 0xff00) | data; + m_isr &= m_imr; + check_interrupts(); + break; + + case REGISTER_VR: + if (LOG) logerror("MC68901 '%s' Interrupt Vector : %x\n", tag(), data & 0xf0); + + m_vr = data & 0xf8; + + if (m_vr & VR_S) + { + if (LOG) logerror("MC68901 '%s' Software End-Of-Interrupt Mode\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Automatic End-Of-Interrupt Mode\n", tag()); + + m_isr = 0; + } + break; + + case REGISTER_TACR: + m_tacr = data & 0x1f; + + switch (m_tacr & 0x0f) + { + case TCR_TIMER_STOPPED: + if (LOG) logerror("MC68901 '%s' Timer A Stopped\n", tag()); + m_timer[TIMER_A]->enable(false); + break; + + case TCR_TIMER_DELAY_4: + case TCR_TIMER_DELAY_10: + case TCR_TIMER_DELAY_16: + case TCR_TIMER_DELAY_50: + case TCR_TIMER_DELAY_64: + case TCR_TIMER_DELAY_100: + case TCR_TIMER_DELAY_200: + { + int divisor = PRESCALER[m_tacr & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer A Delay Mode : %u Prescale\n", tag(), divisor); + m_timer[TIMER_A]->adjust(attotime::from_hz(m_timer_clock / divisor), 0, attotime::from_hz(m_timer_clock / divisor)); + } + break; + + case TCR_TIMER_EVENT: + if (LOG) logerror("MC68901 '%s' Timer A Event Count Mode\n", tag()); + m_timer[TIMER_A]->enable(false); + break; + + case TCR_TIMER_PULSE_4: + case TCR_TIMER_PULSE_10: + case TCR_TIMER_PULSE_16: + case TCR_TIMER_PULSE_50: + case TCR_TIMER_PULSE_64: + case TCR_TIMER_PULSE_100: + case TCR_TIMER_PULSE_200: + { + int divisor = PRESCALER[m_tacr & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer A Pulse Width Mode : %u Prescale\n", tag(), divisor); + m_timer[TIMER_A]->adjust(attotime::never, 0, attotime::from_hz(m_timer_clock / divisor)); + m_timer[TIMER_A]->enable(false); + } + break; + } + + if (m_tacr & TCR_TIMER_RESET) + { + if (LOG) logerror("MC68901 '%s' Timer A Reset\n", tag()); + + m_to[TIMER_A] = 0; + + m_out_tao_func(m_to[TIMER_A]); + } + break; + + case REGISTER_TBCR: + m_tbcr = data & 0x1f; + + switch (m_tbcr & 0x0f) + { + case TCR_TIMER_STOPPED: + if (LOG) logerror("MC68901 '%s' Timer B Stopped\n", tag()); + m_timer[TIMER_B]->enable(false); + break; + + case TCR_TIMER_DELAY_4: + case TCR_TIMER_DELAY_10: + case TCR_TIMER_DELAY_16: + case TCR_TIMER_DELAY_50: + case TCR_TIMER_DELAY_64: + case TCR_TIMER_DELAY_100: + case TCR_TIMER_DELAY_200: + { + int divisor = PRESCALER[m_tbcr & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer B Delay Mode : %u Prescale\n", tag(), divisor); + m_timer[TIMER_B]->adjust(attotime::from_hz(m_timer_clock / divisor), 0, attotime::from_hz(m_timer_clock / divisor)); + } + break; + + case TCR_TIMER_EVENT: + if (LOG) logerror("MC68901 '%s' Timer B Event Count Mode\n", tag()); + m_timer[TIMER_B]->enable(false); + break; + + case TCR_TIMER_PULSE_4: + case TCR_TIMER_PULSE_10: + case TCR_TIMER_PULSE_16: + case TCR_TIMER_PULSE_50: + case TCR_TIMER_PULSE_64: + case TCR_TIMER_PULSE_100: + case TCR_TIMER_PULSE_200: + { + int divisor = PRESCALER[m_tbcr & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer B Pulse Width Mode : %u Prescale\n", tag(), DIVISOR); + m_timer[TIMER_B]->adjust(attotime::never, 0, attotime::from_hz(m_timer_clock / divisor)); + m_timer[TIMER_B]->enable(false); + } + break; + } + + if (m_tacr & TCR_TIMER_RESET) + { + if (LOG) logerror("MC68901 '%s' Timer B Reset\n", tag()); + + m_to[TIMER_B] = 0; + + m_out_tbo_func(m_to[TIMER_B]); + } + break; + + case REGISTER_TCDCR: + m_tcdcr = data & 0x6f; + + switch (m_tcdcr & 0x07) + { + case TCR_TIMER_STOPPED: + if (LOG) logerror("MC68901 '%s' Timer D Stopped\n", tag()); + m_timer[TIMER_D]->enable(false); + break; + + case TCR_TIMER_DELAY_4: + case TCR_TIMER_DELAY_10: + case TCR_TIMER_DELAY_16: + case TCR_TIMER_DELAY_50: + case TCR_TIMER_DELAY_64: + case TCR_TIMER_DELAY_100: + case TCR_TIMER_DELAY_200: + { + int divisor = PRESCALER[m_tcdcr & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer D Delay Mode : %u Prescale\n", tag(), divisor); + m_timer[TIMER_D]->adjust(attotime::from_hz(m_timer_clock / divisor), 0, attotime::from_hz(m_timer_clock / divisor)); + } + break; + } + + switch ((m_tcdcr >> 4) & 0x07) + { + case TCR_TIMER_STOPPED: + if (LOG) logerror("MC68901 '%s' Timer C Stopped\n", tag()); + m_timer[TIMER_C]->enable(false); + break; + + case TCR_TIMER_DELAY_4: + case TCR_TIMER_DELAY_10: + case TCR_TIMER_DELAY_16: + case TCR_TIMER_DELAY_50: + case TCR_TIMER_DELAY_64: + case TCR_TIMER_DELAY_100: + case TCR_TIMER_DELAY_200: + { + int divisor = PRESCALER[(m_tcdcr >> 4) & 0x07]; + if (LOG) logerror("MC68901 '%s' Timer C Delay Mode : %u Prescale\n", tag(), divisor); + m_timer[TIMER_C]->adjust(attotime::from_hz(m_timer_clock / divisor), 0, attotime::from_hz(m_timer_clock / divisor)); + } + break; + } + break; + + case REGISTER_TADR: + if (LOG) logerror("MC68901 '%s' Timer A Data Register : %x\n", tag(), data); + + m_tdr[TIMER_A] = data; + + if (!m_timer[TIMER_A]->enabled()) + { + m_tmc[TIMER_A] = data; + } + break; + + case REGISTER_TBDR: + if (LOG) logerror("MC68901 '%s' Timer B Data Register : %x\n", tag(), data); + + m_tdr[TIMER_B] = data; + + if (!m_timer[TIMER_B]->enabled()) + { + m_tmc[TIMER_B] = data; + } + break; + + case REGISTER_TCDR: + if (LOG) logerror("MC68901 '%s' Timer C Data Register : %x\n", tag(), data); + + m_tdr[TIMER_C] = data; + + if (!m_timer[TIMER_C]->enabled()) + { + m_tmc[TIMER_C] = data; + } + break; + + case REGISTER_TDDR: + if (LOG) logerror("MC68901 '%s' Timer D Data Register : %x\n", tag(), data); + + m_tdr[TIMER_D] = data; + + if (!m_timer[TIMER_D]->enabled()) + { + m_tmc[TIMER_D] = data; + } + break; + + case REGISTER_SCR: + if (LOG) logerror("MC68901 '%s' Sync Character : %x\n", tag(), data); + + m_scr = data; + break; + + case REGISTER_UCR: + if (data & UCR_PARITY_ENABLED) + { + if (data & UCR_PARITY_EVEN) + { + if (LOG) logerror("MC68901 '%s' Parity : Even\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Parity : Odd\n", tag()); + } + } + else + { + if (LOG) logerror("MC68901 '%s' Parity : Disabled\n", tag()); + } + + switch (data & 0x60) + { + case UCR_WORD_LENGTH_8: m_rxtx_word = 8; break; + case UCR_WORD_LENGTH_7: m_rxtx_word = 7; break; + case UCR_WORD_LENGTH_6: m_rxtx_word = 6; break; + case UCR_WORD_LENGTH_5: m_rxtx_word = 5; break; + } + + if (LOG) logerror("MC68901 '%s' Word Length : %u bits\n", tag(), m_rxtx_word); + + switch (data & 0x18) + { + case UCR_START_STOP_0_0: + m_rxtx_start = 0; + m_rxtx_stop = 0; + if (LOG) logerror("MC68901 '%s' Start Bits : 0, Stop Bits : 0, Format : synchronous\n", tag()); + break; + case UCR_START_STOP_1_1: + m_rxtx_start = 1; + m_rxtx_stop = 1; + if (LOG) logerror("MC68901 '%s' Start Bits : 1, Stop Bits : 1, Format : asynchronous\n", tag()); + break; + case UCR_START_STOP_1_15: + m_rxtx_start = 1; + m_rxtx_stop = 1; + if (LOG) logerror("MC68901 '%s' Start Bits : 1, Stop Bits : 1??, Format : asynchronous\n", tag()); + break; + case UCR_START_STOP_1_2: + m_rxtx_start = 1; + m_rxtx_stop = 2; + if (LOG) logerror("MC68901 '%s' Start Bits : 1, Stop Bits : 2, Format : asynchronous\n", tag()); + break; + } + + if (data & UCR_CLOCK_DIVIDE_16) + { + if (LOG) logerror("MC68901 '%s' Rx/Tx Clock Divisor : 16\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Rx/Tx Clock Divisor : 1\n", tag()); + } + + m_ucr = data; + break; + + case REGISTER_RSR: + if ((data & RSR_RCV_ENABLE) == 0) + { + if (LOG) logerror("MC68901 '%s' Receiver Disabled\n", tag()); + m_rsr = 0; + } + else + { + if (LOG) logerror("MC68901 '%s' Receiver Enabled\n", tag()); + + if (data & RSR_SYNC_STRIP_ENABLE) + { + if (LOG) logerror("MC68901 '%s' Sync Strip Enabled\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Sync Strip Disabled\n", tag()); + } + + if (data & RSR_FOUND_SEARCH) + if (LOG) logerror("MC68901 '%s' Receiver Search Mode Enabled\n", tag()); + + m_rsr = data & 0x0b; + } + break; + + case REGISTER_TSR: + if ((data & TSR_XMIT_ENABLE) == 0) + { + if (LOG) logerror("MC68901 '%s' Transmitter Disabled\n", tag()); + + m_tsr = data & 0x27; + } + else + { + if (LOG) logerror("MC68901 '%s' Transmitter Enabled\n", tag()); + + switch (data & 0x06) + { + case TSR_OUTPUT_HI_Z: + if (LOG) logerror("MC68901 '%s' Transmitter Disabled Output State : Hi-Z\n", tag()); + break; + case TSR_OUTPUT_LOW: + if (LOG) logerror("MC68901 '%s' Transmitter Disabled Output State : 0\n", tag()); + break; + case TSR_OUTPUT_HIGH: + if (LOG) logerror("MC68901 '%s' Transmitter Disabled Output State : 1\n", tag()); + break; + case TSR_OUTPUT_LOOP: + if (LOG) logerror("MC68901 '%s' Transmitter Disabled Output State : Loop\n", tag()); + break; + } + + if (data & TSR_BREAK) + { + if (LOG) logerror("MC68901 '%s' Transmitter Break Enabled\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Transmitter Break Disabled\n", tag()); + } + + if (data & TSR_AUTO_TURNAROUND) + { + if (LOG) logerror("MC68901 '%s' Transmitter Auto Turnaround Enabled\n", tag()); + } + else + { + if (LOG) logerror("MC68901 '%s' Transmitter Auto Turnaround Disabled\n", tag()); + } + + m_tsr = data & 0x2f; + m_tsr |= TSR_BUFFER_EMPTY; // x68000 expects the buffer to be empty, so this will do for now + } + break; + + case REGISTER_UDR: + if (LOG) logerror("MC68901 '%s' UDR %x\n", tag(), data); + m_udr = data; + //m_tsr &= ~TSR_BUFFER_EMPTY; + break; + } +} + +WRITE8_MEMBER( mc68901_device::write ) +{ + register_w(offset, data); +} + + +int mc68901_device::get_vector() +{ + int ch; + + for (ch = 15; ch >= 0; ch--) + { + if (BIT(m_imr, ch) && BIT(m_ipr, ch)) + { + if (m_vr & VR_S) + { + /* set interrupt-in-service bit */ + m_isr |= (1 << ch); + } + + /* clear interrupt pending bit */ + m_ipr &= ~(1 << ch); + + check_interrupts(); + + return (m_vr & 0xf0) | ch; + } + } + + return M68K_INT_ACK_SPURIOUS; +} + +WRITE_LINE_MEMBER( mc68901_device::i0_w ) { gpio_input(0, state); } +WRITE_LINE_MEMBER( mc68901_device::i1_w ) { gpio_input(1, state); } +WRITE_LINE_MEMBER( mc68901_device::i2_w ) { gpio_input(2, state); } +WRITE_LINE_MEMBER( mc68901_device::i3_w ) { gpio_input(3, state); } +WRITE_LINE_MEMBER( mc68901_device::i4_w ) { gpio_input(4, state); } +WRITE_LINE_MEMBER( mc68901_device::i5_w ) { gpio_input(5, state); } +WRITE_LINE_MEMBER( mc68901_device::i6_w ) { gpio_input(6, state); } +WRITE_LINE_MEMBER( mc68901_device::i7_w ) { gpio_input(7, state); } + + +WRITE_LINE_MEMBER( mc68901_device::tai_w ) +{ + timer_input(TIMER_A, state); +} + + +WRITE_LINE_MEMBER( mc68901_device::tbi_w ) +{ + timer_input(TIMER_B, state); +} + + +WRITE_LINE_MEMBER( mc68901_device::rc_w ) +{ + if (state) + { + serial_receive(); + } +} + + +WRITE_LINE_MEMBER( mc68901_device::tc_w ) +{ + if (state) + { + serial_transmit(); + } +} |