diff --git a/.gitattributes b/.gitattributes index 8205e5011b8..d26501c931e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -810,6 +810,8 @@ src/emu/machine/mb87078.c svneol=native#text/plain src/emu/machine/mb87078.h svneol=native#text/plain src/emu/machine/mc146818.c svneol=native#text/plain src/emu/machine/mc146818.h svneol=native#text/plain +src/emu/machine/mc68901.c svneol=native#text/plain +src/emu/machine/mc68901.h svneol=native#text/plain src/emu/machine/microtch.c svneol=native#text/plain src/emu/machine/microtch.h svneol=native#text/plain src/emu/machine/msm6242.c svneol=native#text/plain diff --git a/src/emu/emu.mak b/src/emu/emu.mak index 937cf0572b0..ec1e338c071 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -182,6 +182,7 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/mb3773.o \ $(EMUMACHINE)/mb87078.o \ $(EMUMACHINE)/mc146818.o \ + $(EMUMACHINE)/mc68901.o \ $(EMUMACHINE)/microtch.o \ $(EMUMACHINE)/msm6242.o \ $(EMUMACHINE)/nvram.o \ diff --git a/src/emu/machine/mc68901.c b/src/emu/machine/mc68901.c new file mode 100644 index 00000000000..620bcf82f39 --- /dev/null +++ b/src/emu/machine/mc68901.c @@ -0,0 +1,1486 @@ +/********************************************************************** + + 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" + + + +//************************************************************************** +// 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) devcb_call_write_line(&m_out_so_func, _data); + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// devices +const device_type MC68901 = mc68901_device_config::static_alloc_device_config; + + + +//************************************************************************** +// DEVICE CONFIGURATION +//************************************************************************** + +GENERIC_DEVICE_CONFIG_SETUP(mc68901, "Motorola MC68901") + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void mc68901_device_config::device_config_complete() +{ + // inherit a copy of the static data + const mc68901_interface *intf = reinterpret_cast(static_config()); + if (intf != NULL) + *static_cast(this) = *intf; + + // or initialize to defaults if none provided + else + { +// memset(&in_pa_func, 0, sizeof(in_pa_func)); + } +} + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +inline void mc68901_device::check_interrupts() +{ + if (m_ipr & m_imr) + { + devcb_call_write_line(&m_out_irq_func, ASSERT_LINE); + } + else + { + devcb_call_write_line(&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 = devcb_call_read_line(&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: devcb_call_write_line(&m_out_tao_func, m_to[index]); break; + case TIMER_B: devcb_call_write_line(&m_out_tbo_func, m_to[index]); break; + case TIMER_C: devcb_call_write_line(&m_out_tco_func, m_to[index]); break; + case TIMER_D: devcb_call_write_line(&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: + timer_enable(m_timer[index], (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(running_machine &_machine, const mc68901_device_config &config) + : device_t(_machine, config), + m_gpip(0), + m_tsr(TSR_BUFFER_EMPTY), + m_config(config) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mc68901_device::device_start() +{ + /* resolve callbacks */ + devcb_resolve_read8(&m_in_gpio_func, &m_config.in_gpio_func, this); + devcb_resolve_write8(&m_out_gpio_func, &m_config.out_gpio_func, this); + devcb_resolve_read_line(&m_in_si_func, &m_config.in_si_func, this); + devcb_resolve_write_line(&m_out_so_func, &m_config.out_so_func, this); + devcb_resolve_write_line(&m_out_tao_func, &m_config.out_tao_func, this); + devcb_resolve_write_line(&m_out_tbo_func, &m_config.out_tbo_func, this); + devcb_resolve_write_line(&m_out_tco_func, &m_config.out_tco_func, this); + devcb_resolve_write_line(&m_out_tdo_func, &m_config.out_tdo_func, this); + devcb_resolve_write_line(&m_out_irq_func, &m_config.out_irq_func, this); + + /* set initial values */ + m_timer_clock = m_config.timer_clock; + + /* create the timers */ + m_timer[TIMER_A] = device_timer_alloc(*this, TIMER_A); + m_timer[TIMER_B] = device_timer_alloc(*this, TIMER_B); + m_timer[TIMER_C] = device_timer_alloc(*this, TIMER_C); + m_timer[TIMER_D] = device_timer_alloc(*this, TIMER_D); + + if (m_config.rx_clock > 0) + { + m_rx_timer = device_timer_alloc(*this, TIMER_RX); + timer_adjust_periodic(m_rx_timer, attotime_zero, 0, ATTOTIME_IN_HZ(m_config.rx_clock)); + } + + if (m_config.tx_clock > 0) + { + m_tx_timer = device_timer_alloc(*this, TIMER_TX); + timer_adjust_periodic(m_tx_timer, attotime_zero, 0, ATTOTIME_IN_HZ(m_config.tx_clock)); + } + + /* register for state saving */ + state_save_register_device_item(this, 0, m_gpip); + state_save_register_device_item(this, 0, m_aer); + state_save_register_device_item(this, 0, m_ddr); + state_save_register_device_item(this, 0, m_ier); + state_save_register_device_item(this, 0, m_ipr); + state_save_register_device_item(this, 0, m_isr); + state_save_register_device_item(this, 0, m_imr); + state_save_register_device_item(this, 0, m_vr); + state_save_register_device_item(this, 0, m_tacr); + state_save_register_device_item(this, 0, m_tbcr); + state_save_register_device_item(this, 0, m_tcdcr); + state_save_register_device_item_array(this, 0, m_tdr); + state_save_register_device_item_array(this, 0, m_tmc); + state_save_register_device_item_array(this, 0, m_to); + state_save_register_device_item_array(this, 0, m_ti); + state_save_register_device_item(this, 0, m_scr); + state_save_register_device_item(this, 0, m_ucr); + state_save_register_device_item(this, 0, m_rsr); + state_save_register_device_item(this, 0, m_tsr); + state_save_register_device_item(this, 0, m_udr); + state_save_register_device_item(this, 0, m_rx_bits); + state_save_register_device_item(this, 0, m_tx_bits); + state_save_register_device_item(this, 0, m_rx_parity); + state_save_register_device_item(this, 0, m_tx_parity); + state_save_register_device_item(this, 0, m_rx_state); + state_save_register_device_item(this, 0, m_tx_state); + state_save_register_device_item(this, 0, m_rx_buffer); + state_save_register_device_item(this, 0, m_tx_buffer); + state_save_register_device_item(this, 0, m_xmit_state); + state_save_register_device_item(this, 0, m_rxtx_word); + state_save_register_device_item(this, 0, m_rxtx_start); + state_save_register_device_item(this, 0, m_rxtx_stop); + state_save_register_device_item(this, 0, m_rsr_read); + state_save_register_device_item(this, 0, m_next_rsr); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mc68901_device::device_reset() +{ + 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 = devcb_call_read8(&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; + + devcb_call_write8(&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()); + timer_enable(m_timer[TIMER_A], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_A], ATTOTIME_IN_HZ(m_timer_clock / divisor), 0, ATTOTIME_IN_HZ(m_timer_clock / divisor)); + } + break; + + case TCR_TIMER_EVENT: + if (LOG) logerror("MC68901 '%s' Timer A Event Count Mode\n", tag()); + timer_enable(m_timer[TIMER_A], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_A], attotime_never, 0, ATTOTIME_IN_HZ(m_timer_clock / divisor)); + timer_enable(m_timer[TIMER_A], 0); + } + break; + } + + if (m_tacr & TCR_TIMER_RESET) + { + if (LOG) logerror("MC68901 '%s' Timer A Reset\n", tag()); + + m_to[TIMER_A] = 0; + + devcb_call_write_line(&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()); + timer_enable(m_timer[TIMER_B], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_B], ATTOTIME_IN_HZ(m_timer_clock / divisor), 0, ATTOTIME_IN_HZ(m_timer_clock / divisor)); + } + break; + + case TCR_TIMER_EVENT: + if (LOG) logerror("MC68901 '%s' Timer B Event Count Mode\n", tag()); + timer_enable(m_timer[TIMER_B], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_B], attotime_never, 0, ATTOTIME_IN_HZ(m_timer_clock / divisor)); + timer_enable(m_timer[TIMER_B], 0); + } + break; + } + + if (m_tacr & TCR_TIMER_RESET) + { + if (LOG) logerror("MC68901 '%s' Timer B Reset\n", tag()); + + m_to[TIMER_B] = 0; + + devcb_call_write_line(&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()); + timer_enable(m_timer[TIMER_D], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_D], ATTOTIME_IN_HZ(m_timer_clock / divisor), 0, ATTOTIME_IN_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()); + timer_enable(m_timer[TIMER_C], 0); + 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); + timer_adjust_periodic(m_timer[TIMER_C], ATTOTIME_IN_HZ(m_timer_clock / divisor), 0, ATTOTIME_IN_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 (!timer_enabled(m_timer[TIMER_A])) + { + 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 (!timer_enabled(m_timer[TIMER_B])) + { + 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 (!timer_enabled(m_timer[TIMER_C])) + { + 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 (!timer_enabled(m_timer[TIMER_D])) + { + 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(); + } +} diff --git a/src/emu/machine/mc68901.h b/src/emu/machine/mc68901.h new file mode 100644 index 00000000000..9c7e17bf114 --- /dev/null +++ b/src/emu/machine/mc68901.h @@ -0,0 +1,257 @@ +/********************************************************************** + + Motorola MC68901 Multi Function Peripheral emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + _____ _____ + R/_W 1 |* \_/ | 48 _CS + RS1 2 | | 47 _DS + RS2 3 | | 46 _DTACK + RS3 4 | | 45 _IACK + RS4 5 | | 44 D7 + RS5 6 | | 43 D6 + TC 7 | | 42 D5 + SO 8 | | 41 D4 + SI 9 | | 40 D3 + RC 10 | | 39 D2 + Vcc 11 | | 38 D1 + NC 12 | MC68901 | 37 D0 + TAO 13 | MK68901 | 36 GND + TBO 14 | | 35 CLK + TCO 15 | | 34 _IEI + TDO 16 | | 33 _IEO + XTAL1 17 | | 32 _IRQ + XTAL2 18 | | 31 _RR + TAI 19 | | 30 _TR + TBI 20 | | 29 I7 + _RESET 21 | | 28 I6 + I0 22 | | 27 I5 + I1 23 | | 26 I4 + I2 24 |_____________| 25 I3 + +**********************************************************************/ + +#pragma once + +#ifndef __MC68901__ +#define __MC68901__ + +#include "emu.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_MC68901_ADD(_tag, _clock, _config) \ + MCFG_DEVICE_ADD((_tag), MC68901, _clock) \ + MCFG_DEVICE_CONFIG(_config) + +#define MC68901_INTERFACE(name) \ + const mc68901_interface (name) = + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> mc68901_interface + +struct mc68901_interface +{ + int timer_clock; /* timer clock */ + int rx_clock; /* serial receive clock */ + int tx_clock; /* serial transmit clock */ + + devcb_write_line out_irq_func; + + devcb_read8 in_gpio_func; + devcb_write8 out_gpio_func; + + devcb_write_line out_tao_func; + devcb_write_line out_tbo_func; + devcb_write_line out_tco_func; + devcb_write_line out_tdo_func; + + devcb_read_line in_si_func; + devcb_write_line out_so_func; + devcb_write_line out_rr_func; + devcb_write_line out_tr_func; +}; + + + +// ======================> mc68901_device_config + +class mc68901_device_config : public device_config, + public mc68901_interface +{ + friend class mc68901_device; + + // construction/destruction + mc68901_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + +public: + // allocators + static device_config *static_alloc_device_config(const machine_config &mconfig, const char *tag, const device_config *owner, UINT32 clock); + virtual device_t *alloc_device(running_machine &machine) const; + +protected: + // device_config overrides + virtual void device_config_complete(); +}; + + + +// ======================> mc68901_device + +class mc68901_device : public device_t +{ + friend class mc68901_device_config; + + // construction/destruction + mc68901_device(running_machine &_machine, const mc68901_device_config &_config); + +public: + DECLARE_READ8_MEMBER( read ); + DECLARE_WRITE8_MEMBER( write ); + + int get_vector(); + + DECLARE_WRITE_LINE_MEMBER( i0_w ); + DECLARE_WRITE_LINE_MEMBER( i1_w ); + DECLARE_WRITE_LINE_MEMBER( i2_w ); + DECLARE_WRITE_LINE_MEMBER( i3_w ); + DECLARE_WRITE_LINE_MEMBER( i4_w ); + DECLARE_WRITE_LINE_MEMBER( i5_w ); + DECLARE_WRITE_LINE_MEMBER( i6_w ); + DECLARE_WRITE_LINE_MEMBER( i7_w ); + + DECLARE_WRITE_LINE_MEMBER( tai_w ); + DECLARE_WRITE_LINE_MEMBER( tbi_w ); + + DECLARE_WRITE_LINE_MEMBER( rc_w ); + DECLARE_WRITE_LINE_MEMBER( tc_w ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + void check_interrupts(); + void take_interrupt(UINT16 mask); + void rx_buffer_full(); + void rx_error(); + void tx_buffer_empty(); + void tx_error(); + int get_parity_bit(UINT8 b); + void serial_receive(); + void tx_disabled(); + void tx_starting(); + void tx_break(); + void tx_enabled(); + void serial_transmit(); + void timer_count(int index); + void timer_input(int index, int value); + void gpio_input(int bit, int state); + void register_w(offs_t offset, UINT8 data); + +private: + static const device_timer_id TIMER_A = 0; + static const device_timer_id TIMER_B = 1; + static const device_timer_id TIMER_C = 2; + static const device_timer_id TIMER_D = 3; + static const device_timer_id TIMER_RX = 4; + static const device_timer_id TIMER_TX = 5; + + devcb_resolved_read8 m_in_gpio_func; + devcb_resolved_write8 m_out_gpio_func; + devcb_resolved_read_line m_in_si_func; + devcb_resolved_write_line m_out_so_func; + devcb_resolved_write_line m_out_tao_func; + devcb_resolved_write_line m_out_tbo_func; + devcb_resolved_write_line m_out_tco_func; + devcb_resolved_write_line m_out_tdo_func; + devcb_resolved_write_line m_out_irq_func; + + int m_device_type; /* device type */ + int m_timer_clock; /* timer clock */ + + /* registers */ + UINT8 m_gpip; /* general purpose I/O register */ + UINT8 m_aer; /* active edge register */ + UINT8 m_ddr; /* data direction register */ + + UINT16 m_ier; /* interrupt enable register */ + UINT16 m_ipr; /* interrupt pending register */ + UINT16 m_isr; /* interrupt in-service register */ + UINT16 m_imr; /* interrupt mask register */ + UINT8 m_vr; /* vector register */ + + UINT8 m_tacr; /* timer A control register */ + UINT8 m_tbcr; /* timer B control register */ + UINT8 m_tcdcr; /* timers C and D control register */ + UINT8 m_tdr[4]; /* timer data registers */ + + UINT8 m_scr; /* synchronous character register */ + UINT8 m_ucr; /* USART control register */ + UINT8 m_tsr; /* transmitter status register */ + UINT8 m_rsr; /* receiver status register */ + UINT8 m_udr; /* USART data register */ + + /* counter timer state */ + UINT8 m_tmc[4]; /* timer main counters */ + int m_ti[4]; /* timer in latch */ + int m_to[4]; /* timer out latch */ + + /* interrupt state */ + int m_irqlevel; /* interrupt level latch */ + + /* serial state */ + UINT8 m_next_rsr; /* receiver status register latch */ + int m_rsr_read; /* receiver status register read flag */ + int m_rxtx_word; /* word length */ + int m_rxtx_start; /* start bits */ + int m_rxtx_stop; /* stop bits */ + + /* receive state */ + UINT8 m_rx_buffer; /* receive buffer */ + int m_rx_bits; /* receive bit count */ + int m_rx_parity; /* receive parity bit */ + int m_rx_state; /* receive state */ + + /* transmit state */ + UINT8 m_tx_buffer; /* transmit buffer */ + int m_tx_bits; /* transmit bit count */ + int m_tx_parity; /* transmit parity bit */ + int m_tx_state; /* transmit state */ + int m_xmit_state; /* transmitter state */ + + // timers + emu_timer *m_timer[4]; /* counter timers */ + emu_timer *m_rx_timer; /* receive timer */ + emu_timer *m_tx_timer; /* transmit timer */ + + const mc68901_device_config &m_config; +}; + + +// device type definition +extern const device_type MC68901; + + + +#endif