All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v2 02/15] dma: add PC-9821 family interface
@ 2009-10-01 11:55 TAKEDA, toshiya
  0 siblings, 0 replies; only message in thread
From: TAKEDA, toshiya @ 2009-10-01 11:55 UTC (permalink / raw)
  To: qemu-devel

diff --git a/qemu/hw/dma.c b/qemu/hw/dma.c
index f418e42..4f3f0d5 100644
--- a/qemu/hw/dma.c
+++ b/qemu/hw/dma.c
@@ -41,6 +41,7 @@ struct dma_regs {
     uint8_t mode;
     uint8_t page;
     uint8_t pageh;
+    uint8_t bound;
     uint8_t dack;
     uint8_t eop;
     DMA_transfer_handler transfer_handler;
@@ -55,8 +56,11 @@ static struct dma_cont {
     uint8_t command;
     uint8_t mask;
     uint8_t flip_flop;
+    uint8_t access_ctrl;
     int dshift;
     struct dma_regs regs[4];
+    /* NEC PC-98x1 quirks? */
+    uint8_t pc98;
 } dma_controllers[2];
 
 enum {
@@ -74,6 +78,16 @@ enum {
 
 };
 
+enum {
+    MODE_DIR = 0x20,
+};
+
+enum {
+    DISABLE_DMA_OVER_1MB = 0x04,
+};
+
+#define ACCESS_CTRL_INIVAL 0xb4
+
 static void DMA_run (void);
 
 static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
@@ -329,7 +343,8 @@ void DMA_release_DREQ (int nchan)
 static void channel_run (int ncont, int ichan)
 {
     int n;
-    struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+    struct dma_cont *d = &dma_controllers[ncont];
+    struct dma_regs *r = &d->regs[ichan];
 #ifdef DEBUG_DMA
     int dir, opmode;
 
@@ -344,11 +359,26 @@ static void channel_run (int ncont, int ichan)
     }
 #endif
 
-    r = dma_controllers[ncont].regs + ichan;
     n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
                              r->now[COUNT], (r->base[COUNT] + 1) << ncont);
     r->now[COUNT] = n;
     ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+
+    /* increment page register */
+    if (d->pc98 && r->bound) {
+        uint32_t last_addr = r->base[ADDR];
+        if (r->mode & MODE_DIR) {
+            last_addr -= n >> ncont;
+            if (last_addr & 0xffff0000) {
+                r->page = ((r->page - 1) & r->bound) | (r->page & (~r->bound));
+            }
+        } else {
+            last_addr += n >> ncont;
+            if (last_addr & 0xffff0000) {
+                r->page = ((r->page + 1) & r->bound) | (r->page & (~r->bound));
+            }
+        }
+    }
 }
 
 static QEMUBH *dma_bh;
@@ -400,10 +430,14 @@ void DMA_register_channel (int nchan,
 
 int DMA_read_memory (int nchan, void *buf, int pos, int len)
 {
-    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    struct dma_cont *d = &dma_controllers[nchan > 3];
+    struct dma_regs *r = &d->regs[nchan & 3];
     target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
 
-    if (r->mode & 0x20) {
+    if (d->pc98 && (d->access_ctrl & DISABLE_DMA_OVER_1MB)) {
+        addr &= 0xfffff;
+    }
+    if (r->mode & MODE_DIR) {
         int i;
         uint8_t *p = buf;
 
@@ -422,10 +456,14 @@ int DMA_read_memory (int nchan, void *buf, int pos, int len)
 
 int DMA_write_memory (int nchan, void *buf, int pos, int len)
 {
-    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    struct dma_cont *d = &dma_controllers[nchan > 3];
+    struct dma_regs *r = &d->regs[nchan & 3];
     target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
 
-    if (r->mode & 0x20) {
+    if (d->pc98 && (d->access_ctrl & DISABLE_DMA_OVER_1MB)) {
+        addr &= 0xfffff;
+    }
+    if (r->mode & MODE_DIR) {
         int i;
         uint8_t *p = buf;
 
@@ -542,6 +580,8 @@ static const VMStateDescription vmstate_dma = {
 
 void DMA_init (int high_page_enable)
 {
+    dma_controllers[0].pc98 = 0;
+    dma_controllers[1].pc98 = 0;
     dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
               high_page_enable ? 0x480 : -1);
     dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
@@ -551,3 +591,136 @@ void DMA_init (int high_page_enable)
 
     dma_bh = qemu_bh_new(DMA_run_bh, NULL);
 }
+
+/* NEC PC-98x1 */
+
+static void pc98_write_port (void *opaque, uint32_t nport, uint32_t data)
+{
+    static const uint8_t bounds[4] = {0, 0x0f, 0, 0xff};
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    switch (nport) {
+    case 0x01: case 0x03: case 0x05: case 0x07:
+    case 0x09: case 0x0b: case 0x0d: case 0x0f:
+        write_chan (d, nport >> 1, data);
+        break;
+    case 0x11: case 0x13: case 0x15: case 0x17:
+    case 0x19: case 0x1b: case 0x1d: case 0x1f:
+        write_cont (d, nport >> 1, data);
+        break;
+    case 0x21: case 0x23: case 0x25: case 0x27:
+        ichan = ((nport >> 1) + 1) & 3;
+        d->regs[ichan].page = data;
+        break;
+    case 0x29:
+        ichan = data & 3;
+        d->regs[ichan].bound = bounds[(data >> 2) & 3];
+        break;
+    case 0x439:
+        d->access_ctrl = data;
+        break;
+    case 0xe05: case 0xe07: case 0xe09: case 0xe0b:
+        ichan = ((nport >> 1) - 2) & 3;
+        d->regs[ichan].pageh = data;
+        break;
+    }
+}
+
+static uint32_t pc98_read_port (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+
+    switch (nport) {
+    case 0x01: case 0x03: case 0x05: case 0x07:
+    case 0x09: case 0x0b: case 0x0d: case 0x0f:
+        return read_chan (d, nport >> 1);
+    case 0x11: case 0x13: case 0x15: case 0x17:
+    case 0x19: case 0x1b: case 0x1d: case 0x1f:
+        return read_cont (d, nport >> 1);
+    case 0x439:
+        return d->access_ctrl;
+    }
+    return 0;
+}
+
+static void pc98_dma_reset(void *opaque)
+{
+    struct dma_cont *d = opaque;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
+        d->regs[i].bound = 0;
+    }
+    d->access_ctrl = ACCESS_CTRL_INIVAL;
+    dma_reset(d);
+}
+
+static const VMStateDescription vmstate_pc98_dma_regs = {
+    .name = "dma_regs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32_ARRAY(now, struct dma_regs, 2),
+        VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2),
+        VMSTATE_UINT8(mode, struct dma_regs),
+        VMSTATE_UINT8(page, struct dma_regs),
+        VMSTATE_UINT8(pageh, struct dma_regs),
+        VMSTATE_UINT8(bound, struct dma_regs),
+        VMSTATE_UINT8(dack, struct dma_regs),
+        VMSTATE_UINT8(eop, struct dma_regs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pc98_dma = {
+    .name = "dma",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = dma_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(command, struct dma_cont),
+        VMSTATE_UINT8(mask, struct dma_cont),
+        VMSTATE_UINT8(flip_flop, struct dma_cont),
+        VMSTATE_UINT8(access_ctrl, struct dma_cont),
+        VMSTATE_INT32(dshift, struct dma_cont),
+        VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_pc98_dma_regs, struct dma_regs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void pc98_DMA_init (int high_page_enable)
+{
+    struct dma_cont *d = &dma_controllers[0];
+    int i;
+
+    d->pc98 = 1;
+    d->dshift = 0;
+    for (i = 0; i < 8; i++) {
+        register_ioport_write (0x01 + (i << 1), 1, 1, pc98_write_port, d);
+        register_ioport_read (0x01 + (i << 1), 1, 1, pc98_read_port, d);
+        register_ioport_write (0x11 + (i << 1), 1, 1, pc98_write_port, d);
+        register_ioport_read (0x11 + (i << 1), 1, 1, pc98_read_port, d);
+    }
+    for (i = 0; i < 4; i++) {
+        register_ioport_write (0x21 + (i << 1), 1, 1, pc98_write_port, d);
+    }
+    register_ioport_write (0x29, 1, 1, pc98_write_port, d);
+    register_ioport_write (0x439, 1, 1, pc98_write_port, d);
+    register_ioport_read (0x439, 1, 1, pc98_read_port, d);
+    if (high_page_enable) {
+        for (i = 0; i < 4; i++) {
+            register_ioport_write (0xe05 + (i << 1), 1, 1, pc98_write_port, d);
+        }
+    }
+    qemu_register_reset(pc98_dma_reset, d);
+    pc98_dma_reset(d);
+    for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
+        d->regs[i].transfer_handler = dma_phony_handler;
+    }
+    vmstate_register (0, &vmstate_pc98_dma, &dma_controllers[0]);
+
+    dma_bh = qemu_bh_new(DMA_run_bh, NULL);
+}
diff --git a/qemu/hw/isa.h b/qemu/hw/isa.h
index 655ad62..10118b9 100644
--- a/qemu/hw/isa.h
+++ b/qemu/hw/isa.h
@@ -42,6 +42,7 @@ void DMA_hold_DREQ (int nchan);
 void DMA_release_DREQ (int nchan);
 void DMA_schedule(int nchan);
 void DMA_init (int high_page_enable);
+void pc98_DMA_init (int high_page_enable);
 void DMA_register_channel (int nchan,
                            DMA_transfer_handler transfer_handler,
                            void *opaque);

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2009-10-01 11:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-01 11:55 [Qemu-devel] [PATCH v2 02/15] dma: add PC-9821 family interface TAKEDA, toshiya

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.