summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/8237dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/8237dma.c')
-rw-r--r--trunk/src/emu/machine/8237dma.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/8237dma.c b/trunk/src/emu/machine/8237dma.c
new file mode 100644
index 00000000000..c49650987e7
--- /dev/null
+++ b/trunk/src/emu/machine/8237dma.c
@@ -0,0 +1,711 @@
+/**********************************************************************
+
+ 8237 DMA interface and emulation
+
+ The DMA works like this:
+ (summarized from http://www.infran.ru/TechInfo/BSD/handbook258.html#410)
+
+ 1. The device asserts the DRQn line
+ 2. The DMA clears the TC (terminal count) line
+ 3. The DMA asserts the CPU's HRQ (halt request) line
+ 4. Upon acknowledgement of the halt, the DMA will let the device
+ know that it needs to send information by asserting the DACKn
+ line
+ 5. The DMA will read the byte from the device
+ 6. The device clears the DRQn line
+ 7. The DMA clears the CPU's HRQ line
+ 8. (steps 3-7 are repeated for every byte in the chain)
+
+**********************************************************************/
+
+#include "emu.h"
+#include "memconv.h"
+#include "8237dma.h"
+#include "devhelpr.h"
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+#define DMA_MODE_CHANNEL(mode) ((mode) & 0x03)
+#define DMA_MODE_OPERATION(mode) ((mode) & 0x0c)
+#define DMA_MODE_AUTO_INIT(mode) ((mode) & 0x10)
+#define DMA_MODE_DIRECTION(mode) ((mode) & 0x20)
+#define DMA_MODE_TRANSFERMODE(mode) ((mode) & 0xc0)
+
+#define DMA8237_VERIFY_TRANSFER 0x00
+#define DMA8237_WRITE_TRANSFER 0x04
+#define DMA8237_READ_TRANSFER 0x08
+#define DMA8237_ILLEGAL_TRANSFER 0x0c
+
+#define DMA8237_DEMAND_MODE 0x00
+#define DMA8237_SINGLE_MODE 0x40
+#define DMA8237_BLOCK_MODE 0x80
+#define DMA8237_CASCADE_MODE 0xc0
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+const device_type I8237 = &device_creator<i8237_device>;
+
+//-------------------------------------------------
+// i8237_device - constructor
+//-------------------------------------------------
+
+i8237_device::i8237_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, I8237, "Intel 8237", tag, owner, clock)
+{
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void i8237_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const i8237_interface *intf = reinterpret_cast<const i8237_interface *>(static_config());
+ if (intf != NULL)
+ {
+ *static_cast<i8237_interface *>(this) = *intf;
+ }
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_hrq_cb, 0, sizeof(m_out_hrq_cb));
+ memset(&m_out_eop_cb, 0, sizeof(m_out_eop_cb));
+ memset(&m_in_memr_cb, 0, sizeof(m_in_memr_cb));
+ memset(&m_out_memw_cb, 0, sizeof(m_out_memw_cb));
+ memset(&m_in_ior_cb[0], 0, sizeof(m_in_ior_cb[0]));
+ memset(&m_in_ior_cb[1], 0, sizeof(m_in_ior_cb[1]));
+ memset(&m_in_ior_cb[2], 0, sizeof(m_in_ior_cb[2]));
+ memset(&m_in_ior_cb[3], 0, sizeof(m_in_ior_cb[3]));
+ memset(&m_out_iow_cb[0], 0, sizeof(m_out_iow_cb[0]));
+ memset(&m_out_iow_cb[1], 0, sizeof(m_out_iow_cb[1]));
+ memset(&m_out_iow_cb[2], 0, sizeof(m_out_iow_cb[2]));
+ memset(&m_out_iow_cb[3], 0, sizeof(m_out_iow_cb[3]));
+ memset(&m_out_dack_cb[0], 0, sizeof(m_out_dack_cb[0]));
+ memset(&m_out_dack_cb[1], 0, sizeof(m_out_dack_cb[1]));
+ memset(&m_out_dack_cb[2], 0, sizeof(m_out_dack_cb[2]));
+ memset(&m_out_dack_cb[3], 0, sizeof(m_out_dack_cb[3]));
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void i8237_device::device_start()
+{
+ /* resolve callbacks */
+ m_out_hrq_func.resolve(m_out_hrq_cb, *this);
+ m_out_eop_func.resolve(m_out_eop_cb, *this);
+ m_in_memr_func.resolve(m_in_memr_cb, *this);
+ m_out_memw_func.resolve(m_out_memw_cb, *this);
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_chan[i].m_in_ior_func.resolve(m_in_ior_cb[i], *this);
+ m_chan[i].m_out_iow_func.resolve(m_out_iow_cb[i], *this);
+ m_chan[i].m_out_dack_func.resolve(m_out_dack_cb[i], *this);
+ }
+
+ m_timer = machine().scheduler().timer_alloc(FUNC(i8237_timerproc_callback), (void *)this);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void i8237_device::device_reset()
+{
+ m_status = 0x0F;
+ m_eop = 1;
+ m_state = DMA8237_SI;
+ m_last_service_channel = 3;
+
+ m_command = 0;
+ m_drq = 0;
+ m_mask = 0x00;
+ m_status = 0x0F;
+ m_hrq = 0;
+ m_hlda = 0;
+ m_chan[0].m_mode = 0;
+ m_chan[1].m_mode = 0;
+ m_chan[2].m_mode = 0;
+ m_chan[3].m_mode = 0;
+
+ m_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock()));
+}
+
+
+void i8237_device::i8237_do_read()
+{
+ int channel = m_service_channel;
+
+ switch( DMA_MODE_OPERATION( m_chan[ channel ].m_mode ) )
+ {
+ case DMA8237_WRITE_TRANSFER:
+ m_temporary_data = m_chan[channel].m_in_ior_func(0);
+ break;
+ case DMA8237_READ_TRANSFER:
+ m_temporary_data = m_in_memr_func(m_chan[ channel ].m_address);
+ break;
+ case DMA8237_VERIFY_TRANSFER:
+ case DMA8237_ILLEGAL_TRANSFER:
+ break;
+ }
+}
+
+
+void i8237_device::i8237_do_write()
+{
+ int channel = m_service_channel;
+
+ switch( DMA_MODE_OPERATION( m_chan[ channel ].m_mode ) )
+ {
+ case DMA8237_WRITE_TRANSFER:
+ m_out_memw_func(m_chan[ channel ].m_address, m_temporary_data);
+ break;
+ case DMA8237_READ_TRANSFER:
+ m_chan[channel].m_out_iow_func(0, m_temporary_data);
+ break;
+ case DMA8237_VERIFY_TRANSFER:
+ case DMA8237_ILLEGAL_TRANSFER:
+ break;
+ }
+}
+
+
+void i8237_device::i8237_advance()
+{
+ int channel = m_service_channel;
+ int mode = m_chan[channel].m_mode;
+
+ switch ( DMA_MODE_OPERATION( mode ) )
+ {
+ case DMA8237_VERIFY_TRANSFER:
+ case DMA8237_WRITE_TRANSFER:
+ case DMA8237_READ_TRANSFER:
+ m_chan[channel].m_high_address_changed = 0;
+
+ if ( DMA_MODE_DIRECTION( mode ) )
+ {
+ m_chan[channel].m_address -= 1;
+ if ( ( m_chan[channel].m_address & 0xFF ) == 0xFF )
+ {
+ m_chan[channel].m_high_address_changed = 1;
+ }
+ }
+ else
+ {
+ m_chan[channel].m_address += 1;
+ if ( ( m_chan[channel].m_address & 0xFF ) == 0x00 )
+ {
+ m_chan[channel].m_high_address_changed = 1;
+ }
+ }
+
+ m_chan[channel].m_count--;
+
+ if ( m_chan[channel].m_count == 0xFFFF )
+ {
+ /* Set TC bit for this channel */
+ m_status |= ( 0x01 << channel );
+
+ if ( DMA_MODE_AUTO_INIT( mode ) )
+ {
+ m_chan[channel].m_address = m_chan[channel].m_base_address;
+ m_chan[channel].m_count = m_chan[channel].m_base_count;
+ m_chan[channel].m_high_address_changed = 1;
+ }
+ else
+ {
+ m_mask |= ( 0x01 << channel );
+ }
+ }
+ break;
+ case DMA8237_ILLEGAL_TRANSFER:
+ break;
+ }
+}
+
+
+void i8237_device::i8327_set_dack(int channel)
+{
+ for (int i = 0; i < 4; i++)
+ {
+ int state = (i == channel) ^ !BIT(m_command, 7);
+
+ m_chan[i].m_out_dack_func(state);
+ }
+}
+
+
+TIMER_CALLBACK( i8237_device::i8237_timerproc_callback )
+{
+ reinterpret_cast<i8237_device*>(ptr)->i8237_timerproc();
+}
+
+
+void i8237_device::i8237_timerproc()
+{
+ /* Check if operation is disabled */
+ if ( m_command & 0x04 )
+ {
+ return;
+ }
+
+ switch ( m_state )
+ {
+
+ case DMA8237_SI:
+ {
+ /* Make sure EOP is high */
+ if ( !m_eop )
+ {
+ m_eop = 1;
+ m_out_eop_func(m_eop ? ASSERT_LINE : CLEAR_LINE);
+ }
+
+ /* Check if a new DMA request has been received. */
+ /* Bit 6 of the command register determines whether the DREQ signals are active
+ high or active low. */
+ UINT16 pending_request = ( ( m_command & 0x40 ) ? ~m_drq : m_drq ) & ~m_mask;
+
+ if ( pending_request & 0x0f )
+ {
+ int prio_channel = 0;
+
+ /* Determine the channel that should be serviced */
+ int channel = ( m_command & 0x10 ) ? m_last_service_channel : 3;
+ for ( int i = 0; i < 4; i++ )
+ {
+ if ( pending_request & ( 1 << channel ) )
+ {
+ prio_channel = channel;
+ }
+ channel = ( channel - 1 ) & 0x03;
+ }
+
+ /* Store the channel we will be servicing and go to the next state */
+ m_service_channel = prio_channel;
+ m_last_service_channel = prio_channel;
+
+ m_hrq = 1;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_S0;
+
+ m_timer->enable( true );
+ }
+ else if (m_command == 3 && (m_drq & 1))
+ {
+ /* Memory-to-memory transfers */
+ m_hlda = 1;
+ m_state = DMA8237_S0;
+ }
+ else
+ {
+ m_timer->enable( false );
+ }
+ break;
+ }
+
+ case DMA8237_S0:
+ /* S0 is the first of the DMA service. We have requested a hold but are waiting
+ for confirmation. */
+ if ( m_hlda )
+ {
+ if ( DMA_MODE_TRANSFERMODE( m_chan[m_service_channel].m_mode ) == DMA8237_CASCADE_MODE )
+ {
+ /* Cascade Mode, set DACK */
+ i8327_set_dack(m_service_channel);
+
+ /* Wait until peripheral is done */
+ m_state = DMA8237_SC;
+ }
+ else
+ {
+ if ( m_command & 0x01 )
+ {
+ /* Memory-to-memory transfers */
+ m_state = DMA8237_S11;
+ }
+ else
+ {
+ /* Regular transfers */
+ m_state = DMA8237_S1;
+ }
+ }
+ }
+ break;
+
+ case DMA8237_SC: /* Cascade mode, waiting until peripheral is done */
+ if ( ! ( m_drq & ( 0x01 << m_service_channel ) ) )
+ {
+ m_hrq = 0;
+ m_hlda = 0;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_SI;
+
+ /* Clear DACK */
+ i8327_set_dack(-1);
+ }
+
+ /* Not sure if this is correct, documentation is not clear */
+ /* Check if EOP output needs to be asserted */
+ if ( m_status & ( 0x01 << m_service_channel ) )
+ {
+ m_eop = 0;
+ m_out_eop_func(m_eop ? ASSERT_LINE : CLEAR_LINE);
+ }
+ break;
+
+ case DMA8237_S1: /* Output A8-A15 */
+ m_state = DMA8237_S2;
+ break;
+
+ case DMA8237_S2: /* Output A7-A0 */
+ /* set DACK */
+ i8327_set_dack(m_service_channel);
+
+ /* Check for compressed timing */
+ if ( m_command & 0x08 )
+ {
+ m_state = DMA8237_S4;
+ }
+ else
+ {
+ m_state = DMA8237_S3;
+ }
+ break;
+
+ case DMA8237_S3: /* Initiate read */
+ i8237_do_read();
+ m_state = DMA8237_S4;
+ break;
+
+ case DMA8237_S4: /* Perform read/write */
+ /* Perform read when in compressed timing mode */
+ if ( m_command & 0x08 )
+ {
+ i8237_do_read();
+ }
+
+ /* Perform write */
+ i8237_do_write();
+
+
+ /* Advance */
+ i8237_advance();
+
+ {
+ int channel = m_service_channel;
+
+ switch( DMA_MODE_TRANSFERMODE( m_chan[channel].m_mode ) )
+ {
+ case DMA8237_DEMAND_MODE:
+ /* Check for terminal count or EOP signal or DREQ begin de-asserted */
+ if ( ( m_status & ( 0x01 << channel ) ) || !m_eop || !( m_drq & ( 0x01 << channel ) ) )
+ {
+ m_hrq = 0;
+ m_hlda = 0;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_SI;
+ }
+ else
+ {
+ m_state = m_chan[channel].m_high_address_changed ? DMA8237_S1 : DMA8237_S2;
+ }
+ break;
+
+ case DMA8237_SINGLE_MODE:
+ m_hrq = 0;
+ m_hlda = 0;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_SI;
+ break;
+
+ case DMA8237_BLOCK_MODE:
+ /* Check for terminal count or EOP signal */
+ if ( ( m_status & ( 0x01 << channel ) ) || !m_eop )
+ {
+ m_hrq = 0;
+ m_hlda = 0;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_SI;
+ }
+ else
+ {
+ m_state = m_chan[channel].m_high_address_changed ? DMA8237_S1 : DMA8237_S2;
+ }
+ break;
+ }
+
+ /* Check if EOP output needs to be asserted */
+ if ( m_status & ( 0x01 << channel ) )
+ {
+ m_eop = 0;
+ m_out_eop_func(m_eop ? ASSERT_LINE : CLEAR_LINE);
+ }
+ }
+
+ /* clear DACK */
+ if ( m_state == DMA8237_SI )
+ {
+ i8327_set_dack(-1);
+ }
+ break;
+
+ case DMA8237_S11: /* Output A8-A15 */
+
+// logerror("###### dma8237_timerproc %s: from %04x count=%x to %04x count=%x\n", tag(),
+// m_chan[0].m_address, m_chan[0].m_count,
+// m_chan[1].m_address, m_chan[1].m_count);
+
+ // FIXME: this will copy bytes correct, but not 16 bit words
+ m_temporary_data = m_in_memr_func(m_chan[0].m_address);
+ m_out_memw_func(m_chan[1].m_address, m_temporary_data);
+
+ m_service_channel = 0;
+
+ /* Advance */
+ i8237_advance();
+
+ // advance destination channel as well
+ m_chan[1].m_count--;
+ m_chan[1].m_address++;
+
+ if (m_chan[0].m_count == 0xFFFF || m_chan[1].m_count == 0xFFFF) {
+ m_hrq = 0;
+ m_hlda = 0;
+ m_out_hrq_func(m_hrq);
+ m_state = DMA8237_SI;
+ m_status |= 3; // set TC for channel 0 and 1
+ m_drq &= ~3; // clear drq for channel 0 and 1
+
+ // logerror("!!! dma8237_timerproc DMA8237_S11 %s: m_drq=%x m_command=%x\n", tag(), m_drq, m_command);
+ }
+ break;
+ }
+}
+
+
+READ8_DEVICE_HANDLER_TRAMPOLINE(i8237, i8237_r)
+{
+ UINT8 data = 0xFF;
+
+ offset &= 0x0F;
+
+ switch(offset) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ /* DMA address register */
+ data = m_chan[offset / 2].m_address >> (m_msb ? 8 : 0);
+ m_msb ^= 1;
+ break;
+
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ /* DMA count register */
+ data = m_chan[offset / 2].m_count >> (m_msb ? 8 : 0);
+ m_msb ^= 1;
+ break;
+
+ case 8:
+ /* DMA status register */
+ data = (UINT8) m_status;
+
+ /* TC bits are cleared on a status read */
+ m_status &= 0xF0;
+ break;
+
+ case 10:
+ /* DMA mask register */
+ data = m_mask;
+ break;
+
+ case 13:
+ /* DMA master clear */
+ data = m_temp;
+ break;
+
+ case 9: /* DMA write request register */
+ case 11: /* DMA mode register */
+ case 12: /* DMA clear byte pointer flip-flop */
+ case 14: /* DMA clear mask register */
+ case 15: /* DMA write mask register */
+ data = 0xFF;
+ break;
+ }
+
+ return data;
+}
+
+
+WRITE8_DEVICE_HANDLER_TRAMPOLINE(i8237, i8237_w)
+{
+ offset &= 0x0F;
+
+// logerror("i8237_w: offset = %02x, data = %02x\n", offset, data );
+
+ switch(offset) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ {
+ /* DMA address register */
+ int channel = offset / 2;
+ if (m_msb)
+ {
+ m_chan[channel].m_base_address = ( m_chan[channel].m_base_address & 0x00FF ) | ( data << 8 );
+ m_chan[channel].m_address = ( m_chan[channel].m_address & 0x00FF ) | ( data << 8 );
+ }
+ else
+ {
+ m_chan[channel].m_base_address = ( m_chan[channel].m_base_address & 0xFF00 ) | data;
+ m_chan[channel].m_address = ( m_chan[channel].m_address & 0xFF00 ) | data;
+ }
+ m_msb ^= 1;
+ break;
+ }
+
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ {
+ /* DMA count register */
+ int channel = offset / 2;
+ if (m_msb)
+ {
+ m_chan[channel].m_base_count = ( m_chan[channel].m_base_count & 0x00FF ) | ( data << 8 );
+ m_chan[channel].m_count = ( m_chan[channel].m_count & 0x00FF ) | ( data << 8 );
+ }
+ else
+ {
+ m_chan[channel].m_base_count = ( m_chan[channel].m_base_count & 0xFF00 ) | data;
+ m_chan[channel].m_count = ( m_chan[channel].m_count & 0xFF00 ) | data;
+ }
+ m_msb ^= 1;
+ break;
+ }
+
+ case 8:
+ /* DMA command register */
+ m_command = data;
+ m_timer->enable( ( m_command & 0x04 ) ? 0 : 1 );
+ break;
+
+ case 9:
+ {
+ /* DMA request register */
+ int channel = DMA_MODE_CHANNEL(data);
+ if ( data & 0x04 )
+ {
+ m_drq |= 0x01 << channel;
+ m_timer->enable( ( m_command & 0x04 ) ? 0 : 1 );
+ }
+ else
+ {
+ m_status &= ~ ( 0x10 << channel );
+ m_drq &= ~ ( 0x01 << channel );
+ }
+ break;
+ }
+
+ case 10:
+ {
+ /* DMA mask register */
+ int channel = DMA_MODE_CHANNEL(data);
+ if (data & 0x04)
+ {
+ m_mask |= 0x11 << channel;
+ }
+ else
+ {
+ m_mask &= ~(0x11 << channel);
+ }
+ break;
+ }
+
+ case 11:
+ {
+ /* DMA mode register */
+ int channel = DMA_MODE_CHANNEL(data);
+ m_chan[channel].m_mode = data;
+ /* Apparently mode writes also clear the TC bit(?) */
+ m_status &= ~ ( 1 << channel );
+ break;
+ }
+
+ case 12:
+ /* DMA clear byte pointer flip-flop */
+ m_temp = data;
+ m_msb = 0;
+ break;
+
+ case 13:
+ /* DMA master clear */
+ m_msb = 0;
+ m_mask = 0x0f;
+ m_state = DMA8237_SI;
+ m_status &= 0xF0;
+ m_temp = 0;
+ break;
+
+ case 14:
+ /* DMA clear mask register */
+ m_mask &= ~data;
+ m_mask = 0;
+ break;
+
+ case 15:
+ /* DMA write mask register */
+ m_mask = data & 0x0f;
+ break;
+ }
+}
+
+
+void i8237_device::i8237_drq_write(int channel, int state)
+{
+ if (state)
+ {
+ m_drq |= ( 0x01 << channel );
+ }
+ else
+ {
+ m_drq &= ~( 0x01 << channel );
+ }
+
+ m_timer->enable( ( m_command & 0x04 ) ? 0 : 1 );
+
+}
+
+
+
+/***************************************************************************
+ TRAMPOLINES
+***************************************************************************/
+
+WRITE_LINE_DEVICE_HANDLER( i8237_hlda_w ) { downcast<i8237_device*>(device)->i8237_hlda_w(state); }
+WRITE_LINE_DEVICE_HANDLER( i8237_ready_w ) { }
+WRITE_LINE_DEVICE_HANDLER( i8237_dreq0_w ) { downcast<i8237_device*>(device)->i8237_drq_write(0, state); }
+WRITE_LINE_DEVICE_HANDLER( i8237_dreq1_w ) { downcast<i8237_device*>(device)->i8237_drq_write(1, state); }
+WRITE_LINE_DEVICE_HANDLER( i8237_dreq2_w ) { downcast<i8237_device*>(device)->i8237_drq_write(2, state); }
+WRITE_LINE_DEVICE_HANDLER( i8237_dreq3_w ) { downcast<i8237_device*>(device)->i8237_drq_write(3, state); }
+WRITE_LINE_DEVICE_HANDLER( i8237_eop_w ) { }