summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/8237dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/8237dma.c')
-rw-r--r--src/emu/machine/8237dma.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/emu/machine/8237dma.c b/src/emu/machine/8237dma.c
new file mode 100644
index 00000000000..12303219ba9
--- /dev/null
+++ b/src/emu/machine/8237dma.c
@@ -0,0 +1,450 @@
+/**********************************************************************
+
+ 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 "driver.h"
+#include "memconv.h"
+#include "8237dma.h"
+
+struct dma8237
+{
+ const struct dma8237_interface *intf;
+ emu_timer *timer;
+ emu_timer *msbflip_timer;
+
+ struct
+ {
+ UINT16 address;
+ UINT16 count;
+ UINT8 mode;
+ } chan[4];
+
+ UINT32 msb : 1;
+ UINT32 eop : 1;
+ UINT8 temp;
+ UINT8 command;
+ UINT8 drq;
+ UINT8 mask;
+
+ /* bits 0- 3 : Terminal count for channels 0-3
+ * bits 4- 7 : Transfer in progress for channels 0-3 */
+ UINT8 status;
+};
+
+static struct dma8237 *dma;
+static int dma_count;
+
+
+#define DMA_MODE_CHANNEL(mode) ((mode) & 0x03)
+#define DMA_MODE_OPERATION(mode) (((mode) >> 2) & 0x03)
+#define DMA_MODE_DIRECTION(mode) (((mode) & 0x20) ? -1 : +1)
+#define DMA_MODE_TRANSFERMODE(mode) (((mode) >> 6) & 0x03)
+
+
+
+static TIMER_CALLBACK( dma8237_timerproc );
+static TIMER_CALLBACK( dma8237_msbflip_timerproc );
+static void dma8237_update_status(int which);
+
+/* ----------------------------------------------------------------------- */
+
+int dma8237_init(int count)
+{
+ int which;
+
+ dma = auto_malloc(count * sizeof(struct dma8237));
+ memset(dma, 0, count * sizeof(struct dma8237));
+ dma_count = count;
+
+ for (which = 0; which < dma_count; which++)
+ {
+ dma[which].status = 0x0F;
+ dma[which].timer = timer_alloc(dma8237_timerproc);
+ dma[which].msbflip_timer = timer_alloc(dma8237_msbflip_timerproc);
+ dma[which].eop = 1;
+ }
+ return 0;
+}
+
+
+
+void dma8237_config(int which, const struct dma8237_interface *intf)
+{
+ dma[which].intf = intf;
+}
+
+
+
+void dma8237_reset(void)
+{
+ int which;
+
+ for (which = 0; which < dma_count; which++)
+ {
+ dma[which].mask = 0x00;
+ dma[which].status = 0x0F;
+ dma[which].chan[0].mode = 0;
+ dma[which].chan[1].mode = 0;
+ dma[which].chan[2].mode = 0;
+ dma[which].chan[3].mode = 0;
+ dma8237_update_status(which);
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+
+
+static int dma8237_do_operation(int which, int channel)
+{
+ int done;
+ UINT8 data;
+ UINT8 mode;
+
+ mode = dma[which].chan[channel].mode;
+
+ switch(DMA_MODE_OPERATION(mode)) {
+ case 1:
+ data = dma[which].intf->channel_read_func[channel]();
+ dma[which].intf->memory_write_func(channel, dma[which].chan[channel].address, data);
+
+ dma[which].chan[channel].address += DMA_MODE_DIRECTION(mode);
+ dma[which].chan[channel].count--;
+ done = (dma[which].chan[channel].count == 0xFFFF);
+ break;
+
+ case 2:
+ data = dma[which].intf->memory_read_func(channel, dma[which].chan[channel].address);
+ dma[which].intf->channel_write_func[channel](data);
+
+ dma[which].chan[channel].address += DMA_MODE_DIRECTION(mode);
+ dma[which].chan[channel].count--;
+ done = (dma[which].chan[channel].count == 0xFFFF);
+ break;
+
+ default:
+ done = TRUE;
+ break;
+ }
+ return done;
+}
+
+
+
+static TIMER_CALLBACK( dma8237_timerproc )
+{
+ int which = param / 4;
+ int channel = param % 4;
+ int done;
+
+ done = dma8237_do_operation(which, channel);
+
+ if (done)
+ {
+ dma[which].status &= ~(0x10 << channel);
+ dma[which].status |= (0x01 << channel);
+ dma[which].drq &= ~(0x01 << channel);
+ dma8237_update_status(which);
+ }
+}
+
+
+
+static TIMER_CALLBACK( dma8237_msbflip_timerproc )
+{
+ dma[param].msb ^= 1;
+}
+
+
+
+static void dma8237_update_status(int which)
+{
+ UINT16 pending_transfer;
+ int channel;
+ UINT32 new_eop;
+
+ if ((dma[which].status & 0xF0) == 0)
+ {
+ /* no transfer is active right now; is there a transfer pending right now? */
+ pending_transfer = dma[which].drq & ~dma[which].mask;
+
+ if (pending_transfer)
+ {
+ /* we do have a transfer in progress */
+ for (channel = 3; (pending_transfer & (1 << channel)) == 0; channel--)
+ ;
+
+ dma[which].status |= 0x10 << channel;
+ dma[which].status &= ~(0x01 << channel);
+
+ timer_adjust(dma[which].timer,
+ attotime_zero,
+ which * 4 + channel,
+ double_to_attotime(dma[which].intf->bus_speed));
+ }
+ else
+ {
+ /* no transfers active right now */
+ timer_reset(dma[which].timer, attotime_never);
+ }
+
+ /* set the halt line */
+ if (dma[which].intf && dma[which].intf->cpunum >= 0)
+ {
+ cpunum_set_input_line(dma[which].intf->cpunum, INPUT_LINE_HALT,
+ pending_transfer ? ASSERT_LINE : CLEAR_LINE);
+ }
+
+ /* set the eop line, if it has changed */
+ new_eop = (dma[which].status & 0x0F) == 0x0F ? 1 : 0;
+ if (dma[which].eop != new_eop)
+ {
+ dma[which].eop = new_eop;
+ if (dma[which].intf->out_eop_func)
+ dma[which].intf->out_eop_func(new_eop ? ASSERT_LINE : CLEAR_LINE);
+ }
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+
+static void dma8237_verify(int which)
+{
+}
+
+
+
+static void prepare_msb_flip(int which)
+{
+ timer_adjust(dma[which].msbflip_timer, attotime_zero, which, attotime_zero);
+}
+
+
+
+static UINT8 dma8237_read(int which, offs_t offset)
+{
+ UINT8 data = 0xFF;
+ UINT8 mode;
+
+ dma8237_verify(which);
+ offset &= 0x0F;
+
+ switch(offset) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ /* DMA address register */
+ data = dma[which].chan[offset / 2].address >> (dma[which].msb ? 8 : 0);
+ prepare_msb_flip(which);
+
+ /* hack simulating refresh activity for 'ibmxt' BIOS; I do not know
+ * why this is needed; but in any case, the ibmxt driver does not load
+ * if this code is not present */
+ mode = dma[which].chan[0].mode;
+ if ((DMA_MODE_OPERATION(mode) == 2) && (offset == 0))
+ {
+ dma[which].chan[0].address++;
+ dma[which].chan[0].count--;
+ }
+ break;
+
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ /* DMA count register */
+ data = dma[which].chan[offset / 2].count >> (dma[which].msb ? 8 : 0);
+ prepare_msb_flip(which);
+ break;
+
+ case 8:
+ /* DMA status register */
+ data = (UINT8) dma[which].status;
+ break;
+
+ case 10:
+ /* DMA mask register */
+ data = dma[which].mask;
+ break;
+
+ case 13:
+ /* DMA master clear */
+ data = dma[which].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;
+}
+
+
+
+static void dma8237_write(int which, offs_t offset, UINT8 data)
+{
+ int channel;
+
+ dma8237_verify(which);
+ offset &= 0x0F;
+
+ switch(offset) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ /* DMA address register */
+ if (dma[which].msb)
+ dma[which].chan[offset / 2].address |= ((UINT16) data) << 8;
+ else
+ dma[which].chan[offset / 2].address = data;
+ prepare_msb_flip(which);
+ break;
+
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ /* DMA count register */
+ if (dma[which].msb)
+ dma[which].chan[offset / 2].count |= ((UINT16) data) << 8;
+ else
+ dma[which].chan[offset / 2].count = data;
+ prepare_msb_flip(which);
+ break;
+
+ case 8:
+ /* DMA command register */
+ dma[which].command = data;
+ break;
+
+ case 10:
+ /* DMA mask register */
+ channel = DMA_MODE_CHANNEL(data);
+ if (data & 0x04)
+ dma[which].mask |= 0x11 << channel;
+ else
+ dma[which].mask &= ~(0x11 << channel);
+ break;
+
+ case 11:
+ /* DMA mode register */
+ channel = DMA_MODE_CHANNEL(data);
+ dma[which].chan[channel].mode = data;
+ break;
+
+ case 12:
+ /* DMA clear byte pointer flip-flop */
+ dma[which].temp = data;
+ dma[which].msb = 0;
+ break;
+
+ case 13:
+ /* DMA master clear */
+ dma[which].msb = 0;
+ break;
+
+ case 14:
+ /* DMA clear mask register */
+ dma[which].mask &= ~data;
+ dma8237_update_status(which);
+ break;
+
+ case 15:
+ /* DMA write mask register */
+ dma[which].mask |= data;
+ break;
+ }
+}
+
+
+
+static void dma8237_drq_write_callback(int param)
+{
+ int which = param >> 3;
+ int channel = (param >> 1) & 0x03;
+ int state = param & 0x01;
+
+ /* normalize state */
+ if (state)
+ dma[which].drq |= 0x01 << channel;
+ else
+ dma[which].drq &= ~(0x01 << channel);
+
+ dma8237_update_status(which);
+}
+
+
+
+void dma8237_drq_write(int which, int channel, int state)
+{
+ int param;
+
+ param = (which << 3) | (channel << 1) | (state ? 1 : 0);
+ //timer_call_after_resynch(param, dma8237_drq_write_callback);
+ dma8237_drq_write_callback(param);
+}
+
+
+
+/******************* Unfortunate hacks *******************/
+
+void dma8237_run_transfer(int which, int channel)
+{
+ dma[which].status |= 0x10 << channel; /* reset DMA running flag */
+
+ while(!dma8237_do_operation(which, channel))
+ ;
+
+ dma[which].status &= ~(0x10 << channel);
+ dma[which].status |= (0x01 << channel);
+}
+
+
+
+/******************* Standard 8-bit/32-bit/64-bit CPU interfaces *******************/
+
+READ8_HANDLER( dma8237_0_r ) { return dma8237_read(0, offset); }
+READ8_HANDLER( dma8237_1_r ) { return dma8237_read(1, offset); }
+WRITE8_HANDLER( dma8237_0_w ) { dma8237_write(0, offset, data); }
+WRITE8_HANDLER( dma8237_1_w ) { dma8237_write(1, offset, data); }
+
+READ16_HANDLER( dma8237_16le_0_r ) { return read16le_with_read8_handler(dma8237_0_r, offset, mem_mask); }
+READ16_HANDLER( dma8237_16le_1_r ) { return read16le_with_read8_handler(dma8237_1_r, offset, mem_mask); }
+WRITE16_HANDLER( dma8237_16le_0_w ) { write16le_with_write8_handler(dma8237_0_w, offset, data, mem_mask); }
+WRITE16_HANDLER( dma8237_16le_1_w ) { write16le_with_write8_handler(dma8237_1_w, offset, data, mem_mask); }
+
+READ32_HANDLER( dma8237_32le_0_r ) { return read32le_with_read8_handler(dma8237_0_r, offset, mem_mask); }
+READ32_HANDLER( dma8237_32le_1_r ) { return read32le_with_read8_handler(dma8237_1_r, offset, mem_mask); }
+WRITE32_HANDLER( dma8237_32le_0_w ) { write32le_with_write8_handler(dma8237_0_w, offset, data, mem_mask); }
+WRITE32_HANDLER( dma8237_32le_1_w ) { write32le_with_write8_handler(dma8237_1_w, offset, data, mem_mask); }
+
+READ64_HANDLER( dma8237_64be_0_r ) { return read64be_with_read8_handler(dma8237_0_r, offset, mem_mask); }
+READ64_HANDLER( dma8237_64be_1_r ) { return read64be_with_read8_handler(dma8237_1_r, offset, mem_mask); }
+WRITE64_HANDLER( dma8237_64be_0_w ) { write64be_with_write8_handler(dma8237_0_w, offset, data, mem_mask); }
+WRITE64_HANDLER( dma8237_64be_1_w ) { write64be_with_write8_handler(dma8237_1_w, offset, data, mem_mask); }