diff options
Diffstat (limited to 'trunk/src/emu/machine/8257dma.c')
-rw-r--r-- | trunk/src/emu/machine/8257dma.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/8257dma.c b/trunk/src/emu/machine/8257dma.c new file mode 100644 index 00000000000..c6673d087f9 --- /dev/null +++ b/trunk/src/emu/machine/8257dma.c @@ -0,0 +1,454 @@ +/********************************************************************** + + 8257 DMA interface and emulation + + For datasheet http://www.threedee.com/jcm/library/index.html + + 2008/05 Miodrag Milanovic + + - added support for autoload mode + - fixed bug in calculating count + + 2007/11 couriersud + + - architecture copied from 8237 DMA + - significant changes to implementation + + The DMA works like this: + + 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 "8257dma.h" +#include "devhelpr.h" + +#define I8257_STATUS_UPDATE 0x10 +#define I8257_STATUS_TC_CH3 0x08 +#define I8257_STATUS_TC_CH2 0x04 +#define I8257_STATUS_TC_CH1 0x02 +#define I8257_STATUS_TC_CH0 0x01 + +#define DMA_MODE_AUTOLOAD(mode) ((mode) & 0x80) +#define DMA_MODE_TCSTOP(mode) ((mode) & 0x40) +#define DMA_MODE_EXWRITE(mode) ((mode) & 0x20) +#define DMA_MODE_ROTPRIO(mode) ((mode) & 0x10) +#define DMA_MODE_CH_EN(mode, chan) ((mode) & (1 << (chan))) + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +// device type definition +const device_type I8257 = &device_creator<i8257_device>; + +//------------------------------------------------- +// i8257_device - constructor +//------------------------------------------------- + +i8257_device::i8257_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, I8257, "DMA8257", tag, owner, clock), + m_mode(0), + m_rr(0), + m_msb(0), + m_drq(0), + m_status(0x0f) +{ + memset(m_registers, 0, sizeof(m_registers)); + memset(m_address, 0, sizeof(m_address)); + memset(m_count, 0, sizeof(m_count)); + memset(m_rwmode, 0, sizeof(m_rwmode)); +} + + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void i8257_device::device_config_complete() +{ + // inherit a copy of the static data + const i8257_interface *intf = reinterpret_cast<const i8257_interface *>(static_config()); + if (intf != NULL) + { + *static_cast<i8257_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_tc_cb, 0, sizeof(m_out_tc_cb)); + memset(&m_out_mark_cb, 0, sizeof(m_out_mark_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])); + } +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void i8257_device::device_start() +{ + /* validate arguments */ + assert(this != NULL); + + /* resolve callbacks */ + m_out_hrq_func.resolve(m_out_hrq_cb, *this); + m_out_tc_func.resolve(m_out_tc_cb, *this); + m_out_mark_func.resolve(m_out_mark_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 < I8257_NUM_CHANNELS; i++) + { + m_in_ior_func[i].resolve(m_in_ior_cb[i], *this); + m_out_iow_func[i].resolve(m_out_iow_cb[i], *this); + } + + /* set initial values */ + m_timer = timer_alloc(TIMER_OPERATION); + m_msbflip_timer = timer_alloc(TIMER_MSBFLIP); + + /* register for state saving */ + save_item(NAME(m_address)); + save_item(NAME(m_count)); + save_item(NAME(m_rwmode)); + save_item(NAME(m_registers)); + save_item(NAME(m_mode)); + save_item(NAME(m_rr)); + save_item(NAME(m_msb)); + save_item(NAME(m_drq)); + save_item(NAME(m_status)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void i8257_device::device_reset() +{ + m_status &= 0xf0; + m_mode = 0; + i8257_update_status(); +} + + +int i8257_device::i8257_do_operation(int channel) +{ + int done; + UINT8 data; + + UINT8 mode = m_rwmode[channel]; + if (m_count[channel] == 0x0000) + { + m_status |= (0x01 << channel); + + m_out_tc_func(ASSERT_LINE); + } + switch(mode) { + case 1: + if (!m_in_memr_func.isnull()) + { + data = m_in_memr_func(m_address[channel]); + } + else + { + data = 0; + logerror("8257: No memory read function defined.\n"); + } + if (!m_out_iow_func[channel].isnull()) + { + m_out_iow_func[channel](m_address[channel], data); + } + else + { + logerror("8257: No channel write function for channel %d defined.\n",channel); + } + + m_address[channel]++; + m_count[channel]--; + done = (m_count[channel] == 0xFFFF); + break; + + case 2: + if (!m_in_ior_func[channel].isnull()) + { + data = m_in_ior_func[channel](m_address[channel]); + } + else + { + data = 0; + logerror("8257: No channel read function for channel %d defined.\n",channel); + } + + if (!m_out_memw_func.isnull()) + { + m_out_memw_func(m_address[channel], data); + } + else + { + logerror("8257: No memory write function defined.\n"); + } + m_address[channel]++; + m_count[channel]--; + done = (m_count[channel] == 0xFFFF); + break; + case 0: /* verify */ + m_address[channel]++; + m_count[channel]--; + done = (m_count[channel] == 0xFFFF); + break; + default: + fatalerror("i8257_do_operation: invalid mode!\n"); + break; + } + if (done) + { + if ((channel==2) && DMA_MODE_AUTOLOAD(m_mode)) + { + /* in case of autoload at the end channel 3 info is */ + /* copied to channel 2 info */ + m_registers[4] = m_registers[6]; + m_registers[5] = m_registers[7]; + } + + m_out_tc_func(CLEAR_LINE); + } + return done; +} + + +void i8257_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_OPERATION: + { + int i, channel = 0, rr; + int done; + + rr = DMA_MODE_ROTPRIO(m_mode) ? m_rr : 0; + for (i = 0; i < I8257_NUM_CHANNELS; i++) + { + channel = (i + rr) % I8257_NUM_CHANNELS; + if ((m_status & (1 << channel)) == 0) + { + if (m_mode & m_drq & (1 << channel)) + { + break; + } + } + } + done = i8257_do_operation(channel); + + m_rr = (channel + 1) & 0x03; + + if (done) + { + m_drq &= ~(0x01 << channel); + i8257_update_status(); + if (!(DMA_MODE_AUTOLOAD(m_mode) && channel==2)) + { + if (DMA_MODE_TCSTOP(m_mode)) + { + m_mode &= ~(0x01 << channel); + } + } + } + break; + } + + case TIMER_MSBFLIP: + m_msb ^= 1; + break; + + case TIMER_DRQ_SYNC: + { + int channel = param >> 1; + int state = param & 0x01; + + /* normalize state */ + if (state) + { + m_drq |= 0x01 << channel; + m_address[channel] = m_registers[channel * 2]; + m_count[channel] = m_registers[channel * 2 + 1] & 0x3FFF; + m_rwmode[channel] = m_registers[channel * 2 + 1] >> 14; + /* clear channel TC */ + m_status &= ~(0x01 << channel); + } + else + m_drq &= ~(0x01 << channel); + + i8257_update_status(); + break; + } + } +} + + +void i8257_device::i8257_update_status() +{ + UINT16 pending_transfer; + attotime next; + + /* no transfer is active right now; is there a transfer pending right now? */ + pending_transfer = m_drq & (m_mode & 0x0F); + + if (pending_transfer) + { + next = attotime::from_hz(clock() / 4 ); + m_timer->adjust( + attotime::zero, + 0, + /* 1 byte transferred in 4 clock cycles */ + next); + } + else + { + /* no transfers active right now */ + m_timer->reset(); + } + + /* set the halt line */ + m_out_hrq_func(pending_transfer ? ASSERT_LINE : CLEAR_LINE); +} + + +void i8257_device::i8257_prepare_msb_flip() +{ + m_msbflip_timer->adjust(attotime::zero); +} + + +READ8_DEVICE_HANDLER_TRAMPOLINE(i8257, i8257_r) +{ + UINT8 data = 0xFF; + + switch(offset) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* DMA address/count register */ + data = ( m_registers[offset] >> (m_msb ? 8 : 0) ) & 0xFF; + i8257_prepare_msb_flip(); + break; + + case 8: + /* DMA status register */ + data = (UINT8) m_status; + /* read resets status ! */ + m_status &= 0xF0; + + break; + + default: + logerror("8257: Read from register %d.\n", offset); + data = 0xFF; + break; + } + return data; +} + + +WRITE8_DEVICE_HANDLER_TRAMPOLINE(i8257, i8257_w) +{ + switch(offset) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* DMA address/count register */ + if (m_msb) + { + m_registers[offset] |= ((UINT16) data) << 8; + } + else + { + m_registers[offset] = data; + } + + if (DMA_MODE_AUTOLOAD(m_mode)) + { + /* in case of autoload when inserting channel 2 info */ + /* it is automaticaly copied to channel 3 info */ + switch(offset) + { + case 4: + case 5: + if (m_msb) + { + m_registers[offset+2] |= ((UINT16) data) << 8; + } + else + { + m_registers[offset+2] = data; + } + } + } + + i8257_prepare_msb_flip(); + break; + + case 8: + /* DMA mode register */ + m_mode = data; + break; + + default: + logerror("8257: Write to register %d.\n", offset); + break; + } +} + + +void i8257_device::i8257_drq_w(int channel, int state) +{ + int param = (channel << 1) | (state ? 1 : 0); + + synchronize(TIMER_DRQ_SYNC, param); +} + +WRITE_LINE_DEVICE_HANDLER( i8257_hlda_w ) { } +WRITE_LINE_DEVICE_HANDLER( i8257_ready_w ) { } +WRITE_LINE_DEVICE_HANDLER( i8257_drq0_w ) { downcast<i8257_device*>(device)->i8257_drq_w(0, state); } +WRITE_LINE_DEVICE_HANDLER( i8257_drq1_w ) { downcast<i8257_device*>(device)->i8257_drq_w(1, state); } +WRITE_LINE_DEVICE_HANDLER( i8257_drq2_w ) { downcast<i8257_device*>(device)->i8257_drq_w(2, state); } +WRITE_LINE_DEVICE_HANDLER( i8257_drq3_w ) { downcast<i8257_device*>(device)->i8257_drq_w(3, state); } |