summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/z80dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/z80dma.c')
-rw-r--r--trunk/src/emu/machine/z80dma.c887
1 files changed, 887 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/z80dma.c b/trunk/src/emu/machine/z80dma.c
new file mode 100644
index 00000000000..de3132285fd
--- /dev/null
+++ b/trunk/src/emu/machine/z80dma.c
@@ -0,0 +1,887 @@
+/**********************************************************************
+
+ Z80 DMA interface and emulation
+
+ For datasheet http://www.zilog.com/docs/z80/ps0179.pdf
+
+ 2008/01 couriersud
+
+ - architecture copied from 8257 DMA
+ - significant changes to implementation
+ - This is only a minimum implementation to support dkong3 and mario drivers
+ - Only memory to memory is tested!
+
+ TODO:
+ - reset command (C3) is handled improperly
+ - rewrite to match documentation
+ - implement missing features
+ - implement more asserts
+ - implement a INPUT_LINE_BUSREQ for Z80. As a workaround,
+ HALT is used. This implies burst mode.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "memconv.h"
+#include "z80dma.h"
+#include "cpu/z80/z80daisy.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+enum
+{
+ INT_RDY = 0,
+ INT_MATCH,
+ INT_END_OF_BLOCK,
+ INT_MATCH_END_OF_BLOCK
+};
+
+const int COMMAND_RESET = 0xc3;
+const int COMMAND_RESET_PORT_A_TIMING = 0xc7;
+const int COMMAND_RESET_PORT_B_TIMING = 0xcb;
+const int COMMAND_LOAD = 0xcf;
+const int COMMAND_CONTINUE = 0xd3;
+const int COMMAND_DISABLE_INTERRUPTS = 0xaf;
+const int COMMAND_ENABLE_INTERRUPTS = 0xab;
+const int COMMAND_RESET_AND_DISABLE_INTERRUPTS = 0xa3;
+const int COMMAND_ENABLE_AFTER_RETI = 0xb7;
+const int COMMAND_READ_STATUS_BYTE = 0xbf;
+const int COMMAND_REINITIALIZE_STATUS_BYTE = 0x8b;
+const int COMMAND_INITIATE_READ_SEQUENCE = 0xa7;
+const int COMMAND_FORCE_READY = 0xb3;
+const int COMMAND_ENABLE_DMA = 0x87;
+const int COMMAND_DISABLE_DMA = 0x83;
+const int COMMAND_READ_MASK_FOLLOWS = 0xbb;
+
+const int TM_TRANSFER = 0x01;
+const int TM_SEARCH = 0x02;
+const int TM_SEARCH_TRANSFER = 0x03;
+
+
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+#define LOG 0
+#define DMA_LOG 0
+
+#define REGNUM(_m, _s) (((_m)<<3) + (_s))
+#define GET_REGNUM(_r) (&(_r) - &(WR0))
+#define REG(_m, _s) m_regs[REGNUM(_m,_s)]
+#define WR0 REG(0, 0)
+#define WR1 REG(1, 0)
+#define WR2 REG(2, 0)
+#define WR3 REG(3, 0)
+#define WR4 REG(4, 0)
+#define WR5 REG(5, 0)
+#define WR6 REG(6, 0)
+
+#define PORTA_ADDRESS_L REG(0,1)
+#define PORTA_ADDRESS_H REG(0,2)
+
+#define BLOCKLEN_L REG(0,3)
+#define BLOCKLEN_H REG(0,4)
+
+#define PORTA_TIMING REG(1,1)
+#define PORTB_TIMING REG(2,1)
+
+#define MASK_BYTE REG(3,1)
+#define MATCH_BYTE REG(3,2)
+
+#define PORTB_ADDRESS_L REG(4,1)
+#define PORTB_ADDRESS_H REG(4,2)
+#define INTERRUPT_CTRL REG(4,3)
+#define INTERRUPT_VECTOR REG(4,4)
+#define PULSE_CTRL REG(4,5)
+
+#define READ_MASK REG(6,1)
+
+#define PORTA_ADDRESS ((PORTA_ADDRESS_H<<8) | PORTA_ADDRESS_L)
+#define PORTB_ADDRESS ((PORTB_ADDRESS_H<<8) | PORTB_ADDRESS_L)
+#define BLOCKLEN ((BLOCKLEN_H<<8) | BLOCKLEN_L)
+
+#define PORTA_INC (WR1 & 0x10)
+#define PORTB_INC (WR2 & 0x10)
+#define PORTA_FIXED (((WR1 >> 4) & 0x02) == 0x02)
+#define PORTB_FIXED (((WR2 >> 4) & 0x02) == 0x02)
+#define PORTA_MEMORY (((WR1 >> 3) & 0x01) == 0x00)
+#define PORTB_MEMORY (((WR2 >> 3) & 0x01) == 0x00)
+
+#define PORTA_CYCLE_LEN (4-(PORTA_TIMING & 0x03))
+#define PORTB_CYCLE_LEN (4-(PORTB_TIMING & 0x03))
+
+#define PORTA_IS_SOURCE ((WR0 >> 2) & 0x01)
+#define PORTB_IS_SOURCE (!PORTA_IS_SOURCE)
+#define TRANSFER_MODE (WR0 & 0x03)
+
+#define MATCH_F_SET (m_status &= ~0x10)
+#define MATCH_F_CLEAR (m_status |= 0x10)
+#define EOB_F_SET (m_status &= ~0x20)
+#define EOB_F_CLEAR (m_status |= 0x20)
+
+#define READY_ACTIVE_HIGH ((WR5>>3) & 0x01)
+
+#define INTERRUPT_ENABLE (WR3 & 0x20)
+#define INT_ON_MATCH (INTERRUPT_CTRL & 0x01)
+#define INT_ON_END_OF_BLOCK (INTERRUPT_CTRL & 0x02)
+#define INT_ON_READY (INTERRUPT_CTRL & 0x40)
+#define STATUS_AFFECTS_VECTOR (INTERRUPT_CTRL & 0x20)
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+const device_type Z80DMA = &device_creator<z80dma_device>;
+
+//-------------------------------------------------
+// z80dma_device - constructor
+//-------------------------------------------------
+
+z80dma_device::z80dma_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Z80DMA, "Z8410", tag, owner, clock),
+ device_z80daisy_interface(mconfig, *this)
+{
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void z80dma_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const z80dma_interface *intf = reinterpret_cast<const z80dma_interface *>(static_config());
+ if (intf != NULL)
+ *static_cast<z80dma_interface *>(this) = *intf;
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_busreq_cb, 0, sizeof(m_out_busreq_cb));
+ memset(&m_out_int_cb, 0, sizeof(m_out_int_cb));
+ memset(&m_out_bao_cb, 0, sizeof(m_out_bao_cb));
+ memset(&m_in_mreq_cb, 0, sizeof(m_in_mreq_cb));
+ memset(&m_out_mreq_cb, 0, sizeof(m_out_mreq_cb));
+ memset(&m_in_iorq_cb, 0, sizeof(m_in_iorq_cb));
+ memset(&m_out_iorq_cb, 0, sizeof(m_out_iorq_cb));
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void z80dma_device::device_start()
+{
+ // resolve callbacks
+ m_out_busreq_func.resolve(m_out_busreq_cb, *this);
+ m_out_int_func.resolve(m_out_int_cb, *this);
+ m_out_bao_func.resolve(m_out_bao_cb, *this);
+ m_in_mreq_func.resolve(m_in_mreq_cb, *this);
+ m_out_mreq_func.resolve(m_out_mreq_cb, *this);
+ m_in_iorq_func.resolve(m_in_iorq_cb, *this);
+ m_out_iorq_func.resolve(m_out_iorq_cb, *this);
+
+ // allocate timer
+ m_timer = machine().scheduler().timer_alloc(FUNC(static_timerproc), (void *)this);
+
+ // register for state saving
+ save_item(NAME(m_regs));
+ save_item(NAME(m_regs_follow));
+ save_item(NAME(m_num_follow));
+ save_item(NAME(m_cur_follow));
+ save_item(NAME(m_status));
+ save_item(NAME(m_dma_enabled));
+ save_item(NAME(m_vector));
+ save_item(NAME(m_ip));
+ save_item(NAME(m_ius));
+ save_item(NAME(m_addressA));
+ save_item(NAME(m_addressB));
+ save_item(NAME(m_count));
+ save_item(NAME(m_rdy));
+ save_item(NAME(m_force_ready));
+ save_item(NAME(m_is_read));
+ save_item(NAME(m_cur_cycle));
+ save_item(NAME(m_latch));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void z80dma_device::device_reset()
+{
+ m_status = 0;
+ m_rdy = 0;
+ m_force_ready = 0;
+ m_num_follow = 0;
+ m_dma_enabled = 0;
+ m_read_num_follow = m_read_cur_follow = 0;
+ m_reset_pointer = 0;
+
+ // disable interrupts
+ WR3 &= ~0x20;
+ m_ip = 0;
+ m_ius = 0;
+ m_vector = 0;
+
+ update_status();
+}
+
+
+
+//**************************************************************************
+// DAISY CHAIN INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// z80daisy_irq_state - return the overall IRQ
+// state for this device
+//-------------------------------------------------
+
+int z80dma_device::z80daisy_irq_state()
+{
+ int state = 0;
+
+ if (m_ip)
+ {
+ // interrupt pending
+ state = Z80_DAISY_INT;
+ }
+ else if (m_ius)
+ {
+ // interrupt under service
+ state = Z80_DAISY_IEO;
+ }
+
+ if (LOG) logerror("Z80DMA '%s' Interrupt State: %u\n", tag(), state);
+
+ return state;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_ack - acknowledge an IRQ and
+// return the appropriate vector
+//-------------------------------------------------
+
+int z80dma_device::z80daisy_irq_ack()
+{
+ if (m_ip)
+ {
+ if (LOG) logerror("Z80DMA '%s' Interrupt Acknowledge\n", tag());
+
+ // clear interrupt pending flag
+ m_ip = 0;
+ interrupt_check();
+
+ // set interrupt under service flag
+ m_ius = 1;
+
+ // disable DMA
+ m_dma_enabled = 0;
+
+ return m_vector;
+ }
+
+ logerror("z80dma_irq_ack: failed to find an interrupt to ack!\n");
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_reti - clear the interrupt
+// pending state to allow other interrupts through
+//-------------------------------------------------
+
+void z80dma_device::z80daisy_irq_reti()
+{
+ if (m_ius)
+ {
+ if (LOG) logerror("Z80DMA '%s' Return from Interrupt\n", tag());
+
+ // clear interrupt under service flag
+ m_ius = 0;
+ interrupt_check();
+
+ return;
+ }
+
+ logerror("z80dma_irq_reti: failed to find an interrupt to clear IEO on!\n");
+}
+
+
+
+//**************************************************************************
+// INTERNAL STATE MANAGEMENT
+//**************************************************************************
+
+//-------------------------------------------------
+// is_ready - ready for DMA transfer?
+//-------------------------------------------------
+
+int z80dma_device::is_ready()
+{
+ return (m_force_ready) || (m_rdy == READY_ACTIVE_HIGH);
+}
+
+
+//-------------------------------------------------
+// interrupt_check - update IRQ line state
+//-------------------------------------------------
+
+void z80dma_device::interrupt_check()
+{
+ m_out_int_func(m_ip ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// trigger_interrupt - trigger DMA interrupt
+//-------------------------------------------------
+
+void z80dma_device::trigger_interrupt(int level)
+{
+ if (!m_ius && INTERRUPT_ENABLE)
+ {
+ // set interrupt pending flag
+ m_ip = 1;
+
+ // set interrupt vector
+ if (STATUS_AFFECTS_VECTOR)
+ {
+ m_vector = (INTERRUPT_VECTOR & 0xf9) | (level << 1);
+ }
+ else
+ {
+ m_vector = INTERRUPT_VECTOR;
+ }
+
+ m_status &= ~0x08;
+
+ if (LOG) logerror("Z80DMA '%s' Interrupt Pending\n", tag());
+
+ interrupt_check();
+ }
+}
+
+
+//-------------------------------------------------
+// do_read - perform DMA read
+//-------------------------------------------------
+
+void z80dma_device::do_read()
+{
+ UINT8 mode;
+
+ mode = TRANSFER_MODE;
+ switch(mode) {
+ case TM_TRANSFER:
+ case TM_SEARCH:
+ if (PORTA_IS_SOURCE)
+ {
+ if (PORTA_MEMORY)
+ m_latch = m_in_mreq_func(m_addressA);
+ else
+ m_latch = m_in_iorq_func(m_addressA);
+
+ if (DMA_LOG) logerror("Z80DMA '%s' A src: %04x %s -> data: %02x\n", tag(), m_addressA, PORTA_MEMORY ? "mem" : "i/o", m_latch);
+ }
+ else
+ {
+ if (PORTB_MEMORY)
+ m_latch = m_in_mreq_func(m_addressB);
+ else
+ m_latch = m_in_iorq_func(m_addressB);
+
+ if (DMA_LOG) logerror("Z80DMA '%s' B src: %04x %s -> data: %02x\n", tag(), m_addressB, PORTB_MEMORY ? "mem" : "i/o", m_latch);
+ }
+ break;
+ case TM_SEARCH_TRANSFER:
+ fatalerror("z80dma_do_operation: unhandled search & transfer mode !\n");
+ break;
+ default:
+ logerror("z80dma_do_operation: invalid mode %d!\n", mode);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// do_write - perform DMA write
+//-------------------------------------------------
+
+int z80dma_device::do_write()
+{
+ int done = 0;
+ UINT8 mode;
+
+ mode = TRANSFER_MODE;
+ if (m_count == 0x0000)
+ {
+ //FIXME: Any signal here
+ }
+ switch(mode) {
+ case TM_TRANSFER:
+ if (PORTA_IS_SOURCE)
+ {
+ if (PORTB_MEMORY)
+ m_out_mreq_func(m_addressB, m_latch);
+ else
+ m_out_iorq_func(m_addressB, m_latch);
+
+ if (DMA_LOG) logerror("Z80DMA '%s' B dst: %04x %s\n", tag(), m_addressB, PORTB_MEMORY ? "mem" : "i/o");
+ }
+ else
+ {
+ if (PORTA_MEMORY)
+ m_out_mreq_func(m_addressA, m_latch);
+ else
+ m_out_iorq_func(m_addressA, m_latch);
+
+ if (DMA_LOG) logerror("Z80DMA '%s' A dst: %04x %s\n", tag(), m_addressA, PORTA_MEMORY ? "mem" : "i/o");
+ }
+
+ break;
+
+ case TM_SEARCH:
+ {
+ UINT8 load_byte,match_byte;
+ load_byte = m_latch | MASK_BYTE;
+ match_byte = MATCH_BYTE | MASK_BYTE;
+ //if (LOG) logerror("%02x %02x\n",load_byte,match_byte));
+ if (load_byte == match_byte)
+ {
+ if (INT_ON_MATCH)
+ {
+ trigger_interrupt(INT_MATCH);
+ }
+ }
+
+ }
+ break;
+
+ case TM_SEARCH_TRANSFER:
+ fatalerror("z80dma_do_operation: unhandled search & transfer mode !\n");
+ break;
+
+ default:
+ logerror("z80dma_do_operation: invalid mode %d!\n", mode);
+ break;
+ }
+
+ m_addressA += PORTA_FIXED ? 0 : PORTA_INC ? 1 : -1;
+ m_addressB += PORTB_FIXED ? 0 : PORTB_INC ? 1 : -1;
+
+ m_count--;
+ done = (m_count == 0xFFFF); //correct?
+
+ if (done)
+ {
+ //FIXME: interrupt ?
+ }
+ return done;
+}
+
+
+//-------------------------------------------------
+// timerproc
+//-------------------------------------------------
+
+void z80dma_device::timerproc()
+{
+ int done;
+
+ if (--m_cur_cycle)
+ {
+ return;
+ }
+
+ if (m_is_read && !is_ready()) return;
+
+ if (m_is_read)
+ {
+ /* TODO: there's a nasty recursion bug with Alpha for Sharp X1 Turbo on the transfers with this function! */
+ do_read();
+ done = 0;
+ m_is_read = false;
+ m_cur_cycle = (PORTA_IS_SOURCE ? PORTA_CYCLE_LEN : PORTB_CYCLE_LEN);
+ }
+ else
+ {
+ done = do_write();
+ m_is_read = true;
+ m_cur_cycle = (PORTB_IS_SOURCE ? PORTA_CYCLE_LEN : PORTB_CYCLE_LEN);
+ }
+
+ if (done)
+ {
+ m_dma_enabled = 0; //FIXME: Correct?
+ m_status = 0x09;
+
+ m_status |= !is_ready() << 1; // ready line status
+
+ if(TRANSFER_MODE == TM_TRANSFER) m_status |= 0x10; // no match found
+
+ update_status();
+ if (LOG) logerror("Z80DMA '%s' End of Block\n", tag());
+
+ if (INT_ON_END_OF_BLOCK)
+ {
+ trigger_interrupt(INT_END_OF_BLOCK);
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// update_status - update DMA status
+//-------------------------------------------------
+
+void z80dma_device::update_status()
+{
+ UINT16 pending_transfer;
+ attotime next;
+
+ // no transfer is active right now; is there a transfer pending right now?
+ pending_transfer = is_ready() & m_dma_enabled;
+
+ if (pending_transfer)
+ {
+ m_is_read = true;
+ m_cur_cycle = (PORTA_IS_SOURCE ? PORTA_CYCLE_LEN : PORTB_CYCLE_LEN);
+ next = attotime::from_hz(clock());
+ m_timer->adjust(
+ attotime::zero,
+ 0,
+ // 1 byte transferred in 4 clock cycles
+ next);
+ }
+ else
+ {
+ if (m_is_read)
+ {
+ // no transfers active right now
+ m_timer->reset();
+ }
+ }
+
+ // set the busreq line
+ m_out_busreq_func(pending_transfer ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+
+//**************************************************************************
+// READ/WRITE INTERFACES
+//**************************************************************************
+
+//-------------------------------------------------
+// read - register read
+//-------------------------------------------------
+
+UINT8 z80dma_device::read()
+{
+ UINT8 res;
+
+ if(m_read_num_follow == 0) /* TODO: should return the status, but let me know WHAT uses this first */
+ fatalerror("Z80DMA '%s' Read without anything setted into stack", tag());
+
+ res = m_read_regs_follow[m_read_cur_follow];
+ m_read_cur_follow++;
+
+ if(m_read_cur_follow >= m_read_num_follow)
+ m_read_cur_follow = 0;
+
+ if (LOG) logerror("Z80DMA '%s' Read %02x\n", tag(), res);
+
+ return res;
+}
+
+
+//-------------------------------------------------
+// write - register write
+//-------------------------------------------------
+
+void z80dma_device::write(UINT8 data)
+{
+ if (LOG) logerror("Z80DMA '%s' Write %02x\n", tag(), data);
+
+ if (m_num_follow == 0)
+ {
+ m_reset_pointer = 0;
+
+ if ((data & 0x87) == 0) // WR2
+ {
+ WR2 = data;
+ if (data & 0x40)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTB_TIMING);
+ }
+ else if ((data & 0x87) == 0x04) // WR1
+ {
+ WR1 = data;
+ if (data & 0x40)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTA_TIMING);
+ }
+ else if ((data & 0x80) == 0) // WR0
+ {
+ WR0 = data;
+ if (data & 0x08)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTA_ADDRESS_L);
+ if (data & 0x10)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTA_ADDRESS_H);
+ if (data & 0x20)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(BLOCKLEN_L);
+ if (data & 0x40)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(BLOCKLEN_H);
+ }
+ else if ((data & 0x83) == 0x80) // WR3
+ {
+ WR3 = data;
+ if (data & 0x08)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(MASK_BYTE);
+ if (data & 0x10)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(MATCH_BYTE);
+ }
+ else if ((data & 0x83) == 0x81) // WR4
+ {
+ WR4 = data;
+ if (data & 0x04)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTB_ADDRESS_L);
+ if (data & 0x08)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PORTB_ADDRESS_H);
+ if (data & 0x10)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(INTERRUPT_CTRL);
+ }
+ else if ((data & 0xC7) == 0x82) // WR5
+ {
+ WR5 = data;
+ }
+ else if ((data & 0x83) == 0x83) // WR6
+ {
+ m_dma_enabled = 0;
+
+ WR6 = data;
+
+ switch (data)
+ {
+ case COMMAND_ENABLE_AFTER_RETI:
+ fatalerror("Z80DMA '%s' Unimplemented WR6 command %02x", tag(), data);
+ break;
+ case COMMAND_READ_STATUS_BYTE:
+ if (LOG) logerror("Z80DMA '%s' CMD Read status Byte\n", tag());
+ READ_MASK = 1;
+ m_read_regs_follow[m_read_num_follow++] = m_status;
+ break;
+ case COMMAND_RESET_AND_DISABLE_INTERRUPTS:
+ WR3 &= ~0x20;
+ m_ip = 0;
+ m_ius = 0;
+ m_force_ready = 0;
+ m_status |= 0x08;
+ break;
+ case COMMAND_INITIATE_READ_SEQUENCE:
+ if (LOG) logerror("Z80DMA '%s' Initiate Read Sequence\n", tag());
+ m_read_cur_follow = m_read_num_follow = 0;
+ if(READ_MASK & 0x01) { m_read_regs_follow[m_read_num_follow++] = m_status; }
+ if(READ_MASK & 0x02) { m_read_regs_follow[m_read_num_follow++] = m_count & 0xff; } //byte counter (low)
+ if(READ_MASK & 0x04) { m_read_regs_follow[m_read_num_follow++] = m_count >> 8; } //byte counter (high)
+ if(READ_MASK & 0x08) { m_read_regs_follow[m_read_num_follow++] = m_addressA & 0xff; } //port A address (low)
+ if(READ_MASK & 0x10) { m_read_regs_follow[m_read_num_follow++] = m_addressA >> 8; } //port A address (high)
+ if(READ_MASK & 0x20) { m_read_regs_follow[m_read_num_follow++] = m_addressB & 0xff; } //port B address (low)
+ if(READ_MASK & 0x40) { m_read_regs_follow[m_read_num_follow++] = m_addressB >> 8; } //port B address (high)
+ break;
+ case COMMAND_RESET:
+ if (LOG) logerror("Z80DMA '%s' Reset\n", tag());
+ m_dma_enabled = 0;
+ m_force_ready = 0;
+ m_ip = 0;
+ m_ius = 0;
+ interrupt_check();
+ // Needs six reset commands to reset the DMA
+ {
+ UINT8 WRi;
+
+ for(WRi=0;WRi<7;WRi++)
+ REG(WRi,m_reset_pointer) = 0;
+
+ m_reset_pointer++;
+ if(m_reset_pointer >= 6) { m_reset_pointer = 0; }
+ }
+ m_status = 0x38;
+ break;
+ case COMMAND_LOAD:
+ m_force_ready = 0;
+ m_addressA = PORTA_ADDRESS;
+ m_addressB = PORTB_ADDRESS;
+ m_count = BLOCKLEN;
+ m_status |= 0x30;
+
+ if (LOG) logerror("Z80DMA '%s' Load A: %x B: %x N: %x\n", tag(), m_addressA, m_addressB, m_count);
+ break;
+ case COMMAND_DISABLE_DMA:
+ if (LOG) logerror("Z80DMA '%s' Disable DMA\n", tag());
+ m_dma_enabled = 0;
+ break;
+ case COMMAND_ENABLE_DMA:
+ if (LOG) logerror("Z80DMA '%s' Enable DMA\n", tag());
+ m_dma_enabled = 1;
+ update_status();
+ break;
+ case COMMAND_READ_MASK_FOLLOWS:
+ if (LOG) logerror("Z80DMA '%s' Set Read Mask\n", tag());
+ m_regs_follow[m_num_follow++] = GET_REGNUM(READ_MASK);
+ break;
+ case COMMAND_CONTINUE:
+ if (LOG) logerror("Z80DMA '%s' Continue\n", tag());
+ m_count = BLOCKLEN;
+ m_dma_enabled = 1;
+ //"match not found" & "end of block" status flags zeroed here
+ m_status |= 0x30;
+ break;
+ case COMMAND_RESET_PORT_A_TIMING:
+ if (LOG) logerror("Z80DMA '%s' Reset Port A Timing\n", tag());
+ PORTA_TIMING = 0;
+ break;
+ case COMMAND_RESET_PORT_B_TIMING:
+ if (LOG) logerror("Z80DMA '%s' Reset Port B Timing\n", tag());
+ PORTB_TIMING = 0;
+ break;
+ case COMMAND_FORCE_READY:
+ if (LOG) logerror("Z80DMA '%s' Force Ready\n", tag());
+ m_force_ready = 1;
+ update_status();
+ break;
+ case COMMAND_ENABLE_INTERRUPTS:
+ if (LOG) logerror("Z80DMA '%s' Enable IRQ\n", tag());
+ WR3 |= 0x20;
+ break;
+ case COMMAND_DISABLE_INTERRUPTS:
+ if (LOG) logerror("Z80DMA '%s' Disable IRQ\n", tag());
+ WR3 &= ~0x20;
+ break;
+ case COMMAND_REINITIALIZE_STATUS_BYTE:
+ if (LOG) logerror("Z80DMA '%s' Reinitialize status byte\n", tag());
+ m_status |= 0x30;
+ m_ip = 0;
+ break;
+ case 0xFB:
+ if (LOG) logerror("Z80DMA '%s' undocumented command triggered 0x%02X!\n", tag(), data);
+ break;
+ default:
+ fatalerror("Z80DMA '%s' Unknown WR6 command %02x", tag(), data);
+ }
+ }
+ else if(data == 0x8e) //newtype on Sharp X1, unknown purpose
+ printf("Z80DMA '%s' Unknown base register %02x\n", tag(), data);
+ else
+ fatalerror("Z80DMA '%s' Unknown base register %02x", tag(), data);
+ m_cur_follow = 0;
+ }
+ else
+ {
+ int nreg = m_regs_follow[m_cur_follow];
+ m_regs[nreg] = data;
+ m_cur_follow++;
+ if (m_cur_follow>=m_num_follow)
+ m_num_follow = 0;
+ if (nreg == REGNUM(4,3))
+ {
+ m_num_follow=0;
+ if (data & 0x08)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(PULSE_CTRL);
+ if (data & 0x10)
+ m_regs_follow[m_num_follow++] = GET_REGNUM(INTERRUPT_VECTOR);
+ m_cur_follow = 0;
+ }
+ else if(m_regs_follow[m_num_follow] == GET_REGNUM(READ_MASK))
+ {
+ m_read_cur_follow = m_read_num_follow = 0;
+
+ if(READ_MASK & 0x01) { m_read_regs_follow[m_read_num_follow++] = m_status; }
+ if(READ_MASK & 0x02) { m_read_regs_follow[m_read_num_follow++] = m_count & 0xff; } //byte counter (low)
+ if(READ_MASK & 0x04) { m_read_regs_follow[m_read_num_follow++] = m_count >> 8; } //byte counter (high)
+ if(READ_MASK & 0x08) { m_read_regs_follow[m_read_num_follow++] = m_addressA & 0xff; } //port A address (low)
+ if(READ_MASK & 0x10) { m_read_regs_follow[m_read_num_follow++] = m_addressA >> 8; } //port A address (high)
+ if(READ_MASK & 0x20) { m_read_regs_follow[m_read_num_follow++] = m_addressB & 0xff; } //port B address (low)
+ if(READ_MASK & 0x40) { m_read_regs_follow[m_read_num_follow++] = m_addressB >> 8; } //port B address (high)
+ }
+
+ m_reset_pointer++;
+ if(m_reset_pointer >= 6) { m_reset_pointer = 0; }
+ }
+}
+
+
+//-------------------------------------------------
+// rdy_write_callback - deferred RDY signal write
+//-------------------------------------------------
+
+void z80dma_device::rdy_write_callback(int state)
+{
+ // normalize state
+ m_rdy = state;
+ m_status = (m_status & 0xFD) | (!is_ready() << 1);
+
+ update_status();
+
+ if (is_ready() && INT_ON_READY)
+ {
+ trigger_interrupt(INT_RDY);
+ }
+}
+
+
+//-------------------------------------------------
+// rdy_w - ready input
+//-------------------------------------------------
+
+void z80dma_device::rdy_w(int state)
+{
+ if (LOG) logerror("Z80DMA '%s' RDY: %d Active High: %d\n", tag(), state, READY_ACTIVE_HIGH);
+ machine().scheduler().synchronize(FUNC(static_rdy_write_callback), state, (void *)this);
+}
+
+
+//-------------------------------------------------
+// wait_w - wait input
+//-------------------------------------------------
+
+void z80dma_device::wait_w(int state)
+{
+}
+
+
+//-------------------------------------------------
+// bai_w - bus acknowledge input
+//-------------------------------------------------
+
+void z80dma_device::bai_w(int state)
+{
+}
+
+
+
+//**************************************************************************
+// GLOBAL STUBS
+//**************************************************************************
+
+READ8_DEVICE_HANDLER( z80dma_r ) { return downcast<z80dma_device *>(device)->read(); }
+WRITE8_DEVICE_HANDLER( z80dma_w ) { downcast<z80dma_device *>(device)->write(data); }
+
+WRITE_LINE_DEVICE_HANDLER( z80dma_rdy_w ) { downcast<z80dma_device *>(device)->rdy_w(state); }
+WRITE_LINE_DEVICE_HANDLER( z80dma_wait_w ) { downcast<z80dma_device *>(device)->wait_w(state); }
+WRITE_LINE_DEVICE_HANDLER( z80dma_bai_w ) { downcast<z80dma_device *>(device)->bai_w(state); }