qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 03/15] fdc: add PC-9821 family interface


From: TAKEDA, toshiya
Subject: [Qemu-devel] [PATCH v2 03/15] fdc: add PC-9821 family interface
Date: Thu, 01 Oct 2009 20:55:17 +0900

This patch includes VMSTATE_STRUCT_ARRAY_SIZE_UINT8 in hw/hw.h by Mr.Juan 
Quintela,
Thanks Juan!

NEC uPD765A fdc is used in PC-9821 family and some commands are not supported.
So I added version flag to handlers[] to know which commands are supported.


diff --git a/qemu/hw/fdc.c b/qemu/hw/fdc.c
index 389d9e6..40f3062 100644
--- a/qemu/hw/fdc.c
+++ b/qemu/hw/fdc.c
@@ -83,8 +83,10 @@ typedef enum fdisk_flags_t {
 typedef struct fdrive_t {
     BlockDriverState *bs;
     /* Drive status */
+    uint8_t connected;
     fdrive_type_t drive;
     uint8_t perpendicular;    /* 2.88 MB access mode    */
+    uint8_t intr_status;
     /* Position */
     uint8_t head;
     uint8_t track;
@@ -172,6 +174,7 @@ static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t 
track, uint8_t sect,
 static void fd_recalibrate (fdrive_t *drv)
 {
     FLOPPY_DPRINTF("recalibrate\n");
+    drv->intr_status = 0;
     drv->head = 0;
     drv->track = 0;
     drv->sect = 1;
@@ -387,6 +390,8 @@ enum {
 };
 
 enum {
+    FD_SR0_INTRSTAT = 0x01,
+    FD_SR0_NOTRDY   = 0x08,
     FD_SR0_EQPMT    = 0x10,
     FD_SR0_SEEK     = 0x20,
     FD_SR0_ABNTERM  = 0x40,
@@ -424,11 +429,7 @@ enum {
 };
 
 enum {
-#if MAX_FD == 4
     FD_DOR_SELMASK  = 0x03,
-#else
-    FD_DOR_SELMASK  = 0x01,
-#endif
     FD_DOR_nRESET   = 0x04,
     FD_DOR_DMAEN    = 0x08,
     FD_DOR_MOTEN0   = 0x10,
@@ -438,11 +439,7 @@ enum {
 };
 
 enum {
-#if MAX_FD == 4
     FD_TDR_BOOTSEL  = 0x0c,
-#else
-    FD_TDR_BOOTSEL  = 0x04,
-#endif
 };
 
 enum {
@@ -466,10 +463,50 @@ enum {
     FD_DIR_DSKCHG   = 0x80,
 };
 
+enum {
+    PC98_FD_SW_TYP0 = 0x04,
+    PC98_FD_SW_TYP1 = 0x08,
+    PC98_FD_SW_RDY = 0x10,
+    PC98_FD_SW_DMACH = 0x20,
+    PC98_FD_SW_FINT0 = 0x40,
+    PC98_FD_SW_FINT1 = 0x80,
+};
+
+enum {
+    PC98_FD_DOR_MTON = 0x08,
+    PC98_FD_DOR_DMAEN = 0x10,
+    PC98_FD_DOR_AIE = 0x20,
+    PC98_FD_DOR_FRY = 0x40,
+    PC98_FD_DOR_nRESET = 0x80,
+};
+
+enum {
+    PC98_FD_MODE_PORTEXC = 0x01,
+    PC98_FD_MODE_FDDEXC = 0x02,
+    PC98_FD_MODE_EMTON = 0x04,
+};
+
+enum {
+    PC98_FD_MODE1440_MODE = 0x01,
+    PC98_FD_MODE1440_EMODE = 0x10,
+    PC98_FD_MODE1440_DRVSEL = 0x60,
+};
+
 #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
 #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
 #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
 
+/* MAX_LOGICAL_FD determines the max drive number that Intel 82078 can control,
+   and it should be 4.
+   fdctrl->num_floppies determines the number of physical drives that may be
+   connected and it is usually initialized to MAX_FD, but it may be initialized
+   to other value if any system requires */
+
+#define MAX_LOGICAL_FD 4
+
+#define VERSION_INTEL_82078 0x90
+#define VERSION_NEC_UPD765A 0x80
+
 struct fdctrl_t {
     /* Controller's identification */
     uint8_t version;
@@ -508,10 +545,17 @@ struct fdctrl_t {
     /* Power down config (also with status regB access mode */
     uint8_t pwrd;
     /* Sun4m quirks? */
-    int sun4m;
+    uint8_t sun4m;
+    /* PC-98x1 quirks? */
+    uint8_t pc98;
+    qemu_irq irq1200;
+    qemu_irq irq640;
+    uint8_t forced_ready;
+    uint8_t access_mode;
+    uint8_t access_mode1440;
     /* Floppy drives */
     uint8_t num_floppies;
-    fdrive_t drives[MAX_FD];
+    fdrive_t drives[MAX_LOGICAL_FD];
     int reset_sensei;
 };
 
@@ -692,8 +736,8 @@ static const VMStateDescription vmstate_fdc = {
         VMSTATE_UINT8(lock, fdctrl_t),
         VMSTATE_UINT8(pwrd, fdctrl_t),
         VMSTATE_UINT8_EQUAL(num_floppies, fdctrl_t),
-        VMSTATE_STRUCT_ARRAY(drives, fdctrl_t, MAX_FD, 1,
-                             vmstate_fdrive, fdrive_t),
+        VMSTATE_STRUCT_ARRAY_SIZE_UINT8(drives, fdctrl_t, num_floppies, 1,
+                                        vmstate_fdrive, fdrive_t),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -771,7 +815,7 @@ static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
     fdctrl->data_len = 0;
     fdctrl->data_state = 0;
     fdctrl->data_dir = FD_DIR_WRITE;
-    for (i = 0; i < MAX_FD; i++)
+    for (i = 0; i < MAX_LOGICAL_FD; i++)
         fd_recalibrate(&fdctrl->drives[i]);
     fdctrl_reset_fifo(fdctrl);
     if (do_irq) {
@@ -793,7 +837,6 @@ static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
         return &fdctrl->drives[0];
 }
 
-#if MAX_FD == 4
 static inline fdrive_t *drv2 (fdctrl_t *fdctrl)
 {
     if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
@@ -809,17 +852,14 @@ static inline fdrive_t *drv3 (fdctrl_t *fdctrl)
     else
         return &fdctrl->drives[2];
 }
-#endif
 
 static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
 {
     switch (fdctrl->cur_drv) {
         case 0: return drv0(fdctrl);
         case 1: return drv1(fdctrl);
-#if MAX_FD == 4
         case 2: return drv2(fdctrl);
         case 3: return drv3(fdctrl);
-#endif
         default: return NULL;
     }
 }
@@ -969,12 +1009,10 @@ static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
 {
     uint32_t retval = 0;
 
-    if (fdctrl_media_changed(drv0(fdctrl))
-     || fdctrl_media_changed(drv1(fdctrl))
-#if MAX_FD == 4
-     || fdctrl_media_changed(drv2(fdctrl))
-     || fdctrl_media_changed(drv3(fdctrl))
-#endif
+    if ((drv0(fdctrl)->connected && fdctrl_media_changed(drv0(fdctrl)))
+     || (drv1(fdctrl)->connected && fdctrl_media_changed(drv1(fdctrl)))
+     || (drv2(fdctrl)->connected && fdctrl_media_changed(drv2(fdctrl)))
+     || (drv3(fdctrl)->connected && fdctrl_media_changed(drv3(fdctrl)))
         )
         retval |= FD_DIR_DSKCHG;
     if (retval != 0)
@@ -1167,6 +1205,7 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int 
direction)
     if (direction != FD_DIR_WRITE)
         fdctrl->msr |= FD_MSR_DIO;
     /* IO based transfer: calculate len */
+    cur_drv->intr_status = FD_SR0_INTRSTAT;
     fdctrl_raise_irq(fdctrl, 0x00);
 
     return;
@@ -1416,15 +1455,10 @@ static void fdctrl_handle_dumpreg (fdctrl_t *fdctrl, 
int direction)
     fdrive_t *cur_drv = get_cur_drv(fdctrl);
 
     /* Drives position */
-    fdctrl->fifo[0] = drv0(fdctrl)->track;
-    fdctrl->fifo[1] = drv1(fdctrl)->track;
-#if MAX_FD == 4
-    fdctrl->fifo[2] = drv2(fdctrl)->track;
-    fdctrl->fifo[3] = drv3(fdctrl)->track;
-#else
-    fdctrl->fifo[2] = 0;
-    fdctrl->fifo[3] = 0;
-#endif
+    fdctrl->fifo[0] = drv0(fdctrl)->connected ? drv0(fdctrl)->track : 0;
+    fdctrl->fifo[1] = drv1(fdctrl)->connected ? drv1(fdctrl)->track : 0;
+    fdctrl->fifo[2] = drv2(fdctrl)->connected ? drv2(fdctrl)->track : 0;
+    fdctrl->fifo[3] = drv3(fdctrl)->connected ? drv3(fdctrl)->track : 0;
     /* timers */
     fdctrl->fifo[4] = fdctrl->timer0;
     fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 
: 0);
@@ -1454,12 +1488,18 @@ static void fdctrl_handle_restore (fdctrl_t *fdctrl, 
int direction)
     fdrive_t *cur_drv = get_cur_drv(fdctrl);
 
     /* Drives position */
-    drv0(fdctrl)->track = fdctrl->fifo[3];
-    drv1(fdctrl)->track = fdctrl->fifo[4];
-#if MAX_FD == 4
-    drv2(fdctrl)->track = fdctrl->fifo[5];
-    drv3(fdctrl)->track = fdctrl->fifo[6];
-#endif
+    if(drv0(fdctrl)->connected) {
+        drv0(fdctrl)->track = fdctrl->fifo[3];
+    }
+    if(drv1(fdctrl)->connected) {
+        drv1(fdctrl)->track = fdctrl->fifo[4];
+    }
+    if(drv2(fdctrl)->connected) {
+        drv2(fdctrl)->track = fdctrl->fifo[5];
+    }
+    if(drv3(fdctrl)->connected) {
+        drv3(fdctrl)->track = fdctrl->fifo[6];
+    }
     /* timers */
     fdctrl->timer0 = fdctrl->fifo[7];
     fdctrl->timer1 = fdctrl->fifo[8];
@@ -1479,15 +1519,10 @@ static void fdctrl_handle_save (fdctrl_t *fdctrl, int 
direction)
     fdctrl->fifo[0] = 0;
     fdctrl->fifo[1] = 0;
     /* Drives position */
-    fdctrl->fifo[2] = drv0(fdctrl)->track;
-    fdctrl->fifo[3] = drv1(fdctrl)->track;
-#if MAX_FD == 4
-    fdctrl->fifo[4] = drv2(fdctrl)->track;
-    fdctrl->fifo[5] = drv3(fdctrl)->track;
-#else
-    fdctrl->fifo[4] = 0;
-    fdctrl->fifo[5] = 0;
-#endif
+    fdctrl->fifo[2] = drv0(fdctrl)->connected ? drv0(fdctrl)->track : 0;
+    fdctrl->fifo[3] = drv1(fdctrl)->connected ? drv1(fdctrl)->track : 0;
+    fdctrl->fifo[4] = drv2(fdctrl)->connected ? drv2(fdctrl)->track : 0;
+    fdctrl->fifo[5] = drv3(fdctrl)->connected ? drv3(fdctrl)->track : 0;
     /* timers */
     fdctrl->fifo[6] = fdctrl->timer0;
     fdctrl->fifo[7] = fdctrl->timer1;
@@ -1576,6 +1611,13 @@ static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, 
int direction)
     SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
     cur_drv = get_cur_drv(fdctrl);
     fd_recalibrate(cur_drv);
+    if (cur_drv->connected &&
+        ((fdctrl->pc98 && fdctrl->forced_ready) || 
bdrv_is_inserted(cur_drv->bs))
+       ) {
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_SEEK;
+    } else {
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_ABNTERM | FD_SR0_SEEK 
| FD_SR0_NOTRDY;
+    }
     fdctrl_reset_fifo(fdctrl);
     /* Raise Interrupt */
     fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
@@ -1585,21 +1627,47 @@ static void fdctrl_handle_sense_interrupt_status 
(fdctrl_t *fdctrl, int directio
 {
     fdrive_t *cur_drv = get_cur_drv(fdctrl);
 
-    if(fdctrl->reset_sensei > 0) {
-        fdctrl->fifo[0] =
-            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
-        fdctrl->reset_sensei--;
+    if (fdctrl->pc98) {
+        /* NEC uPD765A sends 2 bytes only for each floppy drives
+           that an error occured in recalib or seek command,
+           and sends 1 byte after finished sending each drive status */
+        int drv = -1, i;
+        if (cur_drv->intr_status & FD_SR0_INTRSTAT) {
+            drv = GET_CUR_DRV(fdctrl);
+        } else {
+            for (i = 0; i < MAX_LOGICAL_FD; i++) {
+                if (fdctrl->drives[i].intr_status & FD_SR0_INTRSTAT) {
+                    drv = i;
+                    break;
+                }
+            }
+        }
+        if (drv != -1) {
+            fdctrl->fifo[0] = (fdctrl->drives[drv].intr_status & 0xfc) | drv;
+            fdctrl->fifo[1] = fdctrl->drives[drv].track;
+            fdctrl_set_fifo(fdctrl, 2, 0);
+            fdctrl->drives[drv].intr_status = 0;
+        } else {
+            fdctrl->fifo[0] = FD_SR0_INVCMD;
+            fdctrl_set_fifo(fdctrl, 1, 0);
+        }
+        fdctrl_reset_irq(fdctrl);
     } else {
-        /* XXX: status0 handling is broken for read/write
-           commands, so we do this hack. It should be suppressed
-           ASAP */
-        fdctrl->fifo[0] =
-            FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+        if(fdctrl->reset_sensei > 0) {
+            fdctrl->fifo[0] =
+                FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
+            fdctrl->reset_sensei--;
+        } else {
+            /* XXX: status0 handling is broken for read/write
+               commands, so we do this hack. It should be suppressed
+               ASAP */
+            fdctrl->fifo[0] =
+                FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+        }
+        fdctrl->fifo[1] = cur_drv->track;
+        fdctrl_set_fifo(fdctrl, 2, 0);
+        fdctrl_reset_irq(fdctrl);
     }
-
-    fdctrl->fifo[1] = cur_drv->track;
-    fdctrl_set_fifo(fdctrl, 2, 0);
-    fdctrl_reset_irq(fdctrl);
     fdctrl->status0 = FD_SR0_RDYCHG;
 }
 
@@ -1611,9 +1679,11 @@ static void fdctrl_handle_seek (fdctrl_t *fdctrl, int 
direction)
     cur_drv = get_cur_drv(fdctrl);
     fdctrl_reset_fifo(fdctrl);
     if (fdctrl->fifo[2] > cur_drv->max_track) {
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_ABNTERM | FD_SR0_SEEK;
         fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
     } else {
         cur_drv->track = fdctrl->fifo[2];
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_SEEK;
         /* Raise Interrupt */
         fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
     }
@@ -1680,8 +1750,10 @@ static void fdctrl_handle_relative_seek_out (fdctrl_t 
*fdctrl, int direction)
     cur_drv = get_cur_drv(fdctrl);
     if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
         cur_drv->track = cur_drv->max_track - 1;
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_ABNTERM | FD_SR0_SEEK;
     } else {
         cur_drv->track += fdctrl->fifo[2];
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_SEEK;
     }
     fdctrl_reset_fifo(fdctrl);
     /* Raise Interrupt */
@@ -1696,8 +1768,10 @@ static void fdctrl_handle_relative_seek_in (fdctrl_t 
*fdctrl, int direction)
     cur_drv = get_cur_drv(fdctrl);
     if (fdctrl->fifo[2] > cur_drv->track) {
         cur_drv->track = 0;
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_ABNTERM | FD_SR0_SEEK;
     } else {
         cur_drv->track -= fdctrl->fifo[2];
+        cur_drv->intr_status = FD_SR0_INTRSTAT | FD_SR0_SEEK;
     }
     fdctrl_reset_fifo(fdctrl);
     /* Raise Interrupt */
@@ -1705,6 +1779,7 @@ static void fdctrl_handle_relative_seek_in (fdctrl_t 
*fdctrl, int direction)
 }
 
 static const struct {
+    uint8_t version;
     uint8_t value;
     uint8_t mask;
     const char* name;
@@ -1712,38 +1787,38 @@ static const struct {
     void (*handler)(fdctrl_t *fdctrl, int direction);
     int direction;
 } handlers[] = {
-    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
-    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
-    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
-    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, 
fdctrl_handle_sense_interrupt_status },
-    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
-    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track 
},
-    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, 
FD_DIR_READ },
-    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of 
READ DELETED DATA */
-    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ 
DELETED DATA */
-    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, 
fdctrl_start_transfer_del, FD_DIR_READ },
-    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, 
FD_DIR_SCANE },
-    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
-    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, 
fdctrl_start_transfer, FD_DIR_SCANL },
-    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, 
fdctrl_start_transfer, FD_DIR_SCANH },
-    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, 
fdctrl_start_transfer_del, FD_DIR_WRITE },
-    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
-    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
-    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, 
fdctrl_handle_sense_drive_status },
-    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, 
fdctrl_handle_perpendicular_mode },
-    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
-    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, 
fdctrl_handle_powerdown_mode },
-    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
-    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 
5, fdctrl_handle_drive_specification_command },
-    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, 
fdctrl_handle_relative_seek_out },
-    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, 
fdctrl_unimplemented },
-    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, 
fdctrl_handle_relative_seek_in },
-    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
-    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
-    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
-    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
-    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, 
FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
-    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
+    { 0x80, FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
+    { 0x80, FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, 
FD_DIR_WRITE },
+    { 0x80, FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
+    { 0x80, FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, 
fdctrl_handle_sense_interrupt_status },
+    { 0x80, FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, 
fdctrl_handle_recalibrate },
+    { 0x80, FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, 
fdctrl_handle_format_track },
+    { 0x80, FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, 
FD_DIR_READ },
+    { 0x90, FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* 
part of READ DELETED DATA */
+    { 0x90, FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of 
READ DELETED DATA */
+    { 0x80, FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, 
fdctrl_start_transfer_del, FD_DIR_READ },
+    { 0x80, FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, 
FD_DIR_SCANE },
+    { 0x90, FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
+    { 0x80, FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, 
fdctrl_start_transfer, FD_DIR_SCANL },
+    { 0x80, FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, 
fdctrl_start_transfer, FD_DIR_SCANH },
+    { 0x80, FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, 
fdctrl_start_transfer_del, FD_DIR_WRITE },
+    { 0x80, FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
+    { 0x80, FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
+    { 0x80, FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, 
fdctrl_handle_sense_drive_status },
+    { 0x90, FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, 
fdctrl_handle_perpendicular_mode },
+    { 0x90, FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
+    { 0x90, FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, 
fdctrl_handle_powerdown_mode },
+    { 0x90, FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
+    { 0x90, FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION 
COMMAND", 5, fdctrl_handle_drive_specification_command },
+    { 0x90, FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, 
fdctrl_handle_relative_seek_out },
+    { 0x90, FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, 
fdctrl_unimplemented },
+    { 0x90, FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, 
fdctrl_handle_relative_seek_in },
+    { 0x90, FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
+    { 0x90, FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
+    { 0x80, FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
+    { 0x90, FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
+    { 0x90, FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, 
FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
+    { 0, 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
 };
 /* Associate command to an index in the 'handlers' array */
 static uint8_t command_to_handler[256];
@@ -1828,13 +1903,219 @@ static void fdctrl_result_timer(void *opaque)
     fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
 }
 
+/* NEC PC-98x1 */
+
+static int pc98_fdc_post_load(void *opaque)
+{
+    fdctrl_t *fdctrl = opaque;
+
+    if (fdctrl->access_mode & PC98_FD_MODE_PORTEXC) {
+        fdctrl->irq = fdctrl->irq1200;
+        fdctrl->dma_chann = 2;
+    } else {
+        fdctrl->irq = fdctrl->irq640;
+        fdctrl->dma_chann = 3;
+    }
+    fdc_post_load(fdctrl);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_pc98_fdrive = {
+    .name = "fdrive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(head, fdrive_t),
+        VMSTATE_UINT8(track, fdrive_t),
+        VMSTATE_UINT8(sect, fdrive_t),
+        /* PC-98x1 */
+        VMSTATE_UINT8(intr_status, fdrive_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pc98_fdc = {
+    .name = "fdc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = fdc_pre_save,
+    .post_load = pc98_fdc_post_load,
+    .fields      = (VMStateField []) {
+        /* Controller State */
+        VMSTATE_UINT8(sra, fdctrl_t),
+        VMSTATE_UINT8(srb, fdctrl_t),
+        VMSTATE_UINT8(dor_vmstate, fdctrl_t),
+        VMSTATE_UINT8(tdr, fdctrl_t),
+        VMSTATE_UINT8(dsr, fdctrl_t),
+        VMSTATE_UINT8(msr, fdctrl_t),
+        VMSTATE_UINT8(status0, fdctrl_t),
+        VMSTATE_UINT8(status1, fdctrl_t),
+        VMSTATE_UINT8(status2, fdctrl_t),
+        /* Command FIFO */
+        VMSTATE_VARRAY(fifo, fdctrl_t, fifo_size, 0, vmstate_info_uint8, 
uint8),
+        VMSTATE_UINT32(data_pos, fdctrl_t),
+        VMSTATE_UINT32(data_len, fdctrl_t),
+        VMSTATE_UINT8(data_state, fdctrl_t),
+        VMSTATE_UINT8(data_dir, fdctrl_t),
+        VMSTATE_UINT8(eot, fdctrl_t),
+        /* States kept only to be returned back */
+        VMSTATE_UINT8(timer0, fdctrl_t),
+        VMSTATE_UINT8(timer1, fdctrl_t),
+        VMSTATE_UINT8(precomp_trk, fdctrl_t),
+        VMSTATE_UINT8(config, fdctrl_t),
+        VMSTATE_UINT8(lock, fdctrl_t),
+        VMSTATE_UINT8(pwrd, fdctrl_t),
+        VMSTATE_UINT8_EQUAL(num_floppies, fdctrl_t),
+        VMSTATE_STRUCT_ARRAY_SIZE_UINT8(drives, fdctrl_t, num_floppies, 1,
+                                        vmstate_fdrive, fdrive_t),
+        /* PC-98x1 */
+        VMSTATE_UINT8(forced_ready, fdctrl_t),
+        VMSTATE_UINT8(access_mode, fdctrl_t),
+        VMSTATE_UINT8(access_mode1440, fdctrl_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static uint32_t pc98_fdctrl_read_port (void *opaque, uint32_t reg)
+{
+    fdctrl_t *fdctrl = opaque;
+    fdrive_t *cur_drv = get_cur_drv(fdctrl);
+    uint32_t value = 0xff;
+    uint8_t bit;
+
+    switch (reg) {
+    case 0x90:
+    case 0xc8:
+        value = fdctrl_read_main_status(fdctrl);
+        break;
+    case 0x92:
+    case 0xca:
+        value = fdctrl_read_data(fdctrl);
+        break;
+    case 0x94:
+    case 0xcc:
+        value = PC98_FD_SW_TYP0 | PC98_FD_SW_FINT0;
+        if (!(fdctrl->access_mode & PC98_FD_MODE_PORTEXC)) {
+            if (cur_drv->connected && bdrv_is_inserted(cur_drv->bs)) {
+                value |= PC98_FD_SW_RDY;
+            }
+            value |= PC98_FD_SW_DMACH;
+        }
+        break;
+    case 0xbe:
+        value = 0xf8 | (fdctrl->access_mode & (PC98_FD_MODE_FDDEXC | 
PC98_FD_MODE_PORTEXC));
+        break;
+    case 0x4be:
+        bit = 1 << ((fdctrl->access_mode1440 & PC98_FD_MODE1440_DRVSEL) >> 5);
+        if (fdctrl->access_mode1440 & bit) {
+            value = 0xff;
+        } else {
+            value = 0xfe;
+        }
+        break;
+    }
+    return value;
+}
+
+static void pc98_fdctrl_write_port (void *opaque, uint32_t reg, uint32_t value)
+{
+    fdctrl_t *fdctrl = opaque;
+
+    switch (reg) {
+    case 0x92:
+    case 0xca:
+        fdctrl_write_data(fdctrl, value);
+        break;
+    case 0x94:
+    case 0xcc:
+        if (!(value & PC98_FD_DOR_nRESET)) {
+            if (fdctrl->dor & FD_DOR_nRESET) {
+                FLOPPY_DPRINTF("controller enter RESET state\n");
+            }
+            fdctrl->dor &= ~FD_DOR_nRESET;
+        } else {
+            if (!(fdctrl->dor & FD_DOR_nRESET)) {
+                FLOPPY_DPRINTF("controller out of RESET state\n");
+                fdctrl_reset(fdctrl, 1);
+                fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+            }
+            fdctrl->dor |= FD_DOR_nRESET;
+        }
+        if (fdctrl->access_mode & PC98_FD_MODE_PORTEXC) {
+            fdctrl->forced_ready = ((value & PC98_FD_DOR_FRY) != 0);
+            if (fdctrl->access_mode & PC98_FD_MODE_EMTON) {
+                if (value & PC98_FD_DOR_MTON) {
+                    fdctrl->dor |= (FD_DOR_MOTEN0 | FD_DOR_MOTEN1);
+                    fdctrl->srb |= (FD_SRB_MTR0 | FD_SRB_MTR1);
+                } else {
+                    fdctrl->dor &= ~(FD_DOR_MOTEN0 | FD_DOR_MOTEN1);
+                    fdctrl->srb &= ~(FD_SRB_MTR0 | FD_SRB_MTR1);
+                }
+            }
+        } else {
+            if (value & PC98_FD_DOR_AIE) {
+                fdctrl->forced_ready = ((value & PC98_FD_DOR_FRY) != 0);
+            }
+            if (value & PC98_FD_DOR_DMAEN) {
+                fdctrl->dor |= FD_DOR_DMAEN;
+            } else {
+                fdctrl->dor &= ~FD_DOR_DMAEN;
+            }
+            if (value & PC98_FD_DOR_MTON) {
+                fdctrl->dor |= (FD_DOR_MOTEN0 | FD_DOR_MOTEN1);
+                fdctrl->srb |= (FD_SRB_MTR0 | FD_SRB_MTR1);
+            } else {
+                fdctrl->dor &= ~(FD_DOR_MOTEN0 | FD_DOR_MOTEN1);
+                fdctrl->srb &= ~(FD_SRB_MTR0 | FD_SRB_MTR1);
+            }
+        }
+        break;
+    case 0xbe:
+        fdctrl->access_mode = value;
+        if (fdctrl->access_mode & PC98_FD_MODE_PORTEXC) {
+            if (!(fdctrl->access_mode & PC98_FD_MODE_EMTON)) {
+                fdctrl->dor |= (FD_DOR_MOTEN0 | FD_DOR_MOTEN1);
+                fdctrl->srb |= (FD_SRB_MTR0 | FD_SRB_MTR1);
+            }
+            fdctrl->dor |= FD_DOR_DMAEN;
+            fdctrl->irq = fdctrl->irq1200;
+            fdctrl->dma_chann = 2;
+        } else {
+            fdctrl->irq = fdctrl->irq640;
+            fdctrl->dma_chann = 3;
+        }
+        break;
+    case 0x4be:
+        if (value & PC98_FD_MODE1440_EMODE) {
+            uint8_t bit = 1 << ((value & PC98_FD_MODE1440_DRVSEL) >> 5);
+            if (value & PC98_FD_MODE1440_MODE) {
+                fdctrl->access_mode1440 |= bit;
+            } else {
+                fdctrl->access_mode1440 &= ~bit;
+            }
+        }
+        fdctrl->access_mode1440 &= ~PC98_FD_MODE1440_DRVSEL;
+        fdctrl->access_mode1440 |= (value & PC98_FD_MODE1440_DRVSEL);
+        break;
+    }
+}
+
 /* Init functions */
 static void fdctrl_connect_drives(fdctrl_t *fdctrl, BlockDriverState **fds)
 {
     unsigned int i;
 
-    for (i = 0; i < MAX_FD; i++) {
-        fd_init(&fdctrl->drives[i], fds[i]);
+    for (i = 0; i < MAX_LOGICAL_FD; i++) {
+        if (i < fdctrl->num_floppies) {
+            fdctrl->drives[i].connected = 1;
+            fd_init(&fdctrl->drives[i], fds[i]);
+        } else {
+            fdctrl->drives[i].connected = 0;
+            fd_init(&fdctrl->drives[i], NULL);
+        }
         fd_revalidate(&fdctrl->drives[i]);
     }
 }
@@ -1851,6 +2132,7 @@ fdctrl_t *fdctrl_init_isa(BlockDriverState **fds)
     fdctrl->dma_chann = dma_chann;
     DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
 
+    fdctrl->num_floppies = MAX_FD;
     fdctrl_connect_drives(fdctrl, fds);
 
     return fdctrl;
@@ -1873,6 +2155,8 @@ fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
 
     fdctrl->dma_chann = dma_chann;
     DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
+
+    fdctrl->num_floppies = MAX_FD;
     fdctrl_connect_drives(fdctrl, fds);
 
     return fdctrl;
@@ -1895,6 +2179,29 @@ fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, 
target_phys_addr_t io_base,
 
     fdctrl->dma_chann = -1;
 
+    fdctrl->num_floppies = MAX_FD;
+    fdctrl_connect_drives(fdctrl, fds);
+
+    return fdctrl;
+}
+
+fdctrl_t *pc98_fdctrl_init(BlockDriverState **fds)
+{
+    fdctrl_t *fdctrl;
+    ISADevice *dev;
+
+    dev = isa_create_simple("pc98-fdc");
+    fdctrl = &(DO_UPCAST(fdctrl_isabus_t, busdev, dev)->state);
+
+    fdctrl->access_mode = PC98_FD_MODE_FDDEXC | PC98_FD_MODE_PORTEXC;
+    fdctrl->dor |= FD_DOR_DMAEN;
+
+    fdctrl->irq = fdctrl->irq1200;
+    fdctrl->dma_chann = 2;
+    DMA_register_channel(2, &fdctrl_transfer_handler, fdctrl);
+    DMA_register_channel(3, &fdctrl_transfer_handler, fdctrl);
+
+    fdctrl->num_floppies = MAX_FD;
     fdctrl_connect_drives(fdctrl, fds);
 
     return fdctrl;
@@ -1909,9 +2216,11 @@ static int fdctrl_init_common(fdctrl_t *fdctrl)
     if (!command_tables_inited) {
         command_tables_inited = 1;
         for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) {
-            for (j = 0; j < sizeof(command_to_handler); j++) {
-                if ((j & handlers[i].mask) == handlers[i].value) {
-                    command_to_handler[j] = i;
+            if (handlers[i].version <= fdctrl->version) {
+                for (j = 0; j < sizeof(command_to_handler); j++) {
+                    if ((j & handlers[i].mask) == handlers[i].value) {
+                        command_to_handler[j] = i;
+                    }
                 }
             }
         }
@@ -1923,12 +2232,9 @@ static int fdctrl_init_common(fdctrl_t *fdctrl)
     fdctrl->result_timer = qemu_new_timer(vm_clock,
                                           fdctrl_result_timer, fdctrl);
 
-    fdctrl->version = 0x90; /* Intel 82078 controller */
     fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, 
polling & FIFO enabled */
-    fdctrl->num_floppies = MAX_FD;
 
     fdctrl_external_reset(fdctrl);
-    vmstate_register(-1, &vmstate_fdc, fdctrl);
     qemu_register_reset(fdctrl_external_reset, fdctrl);
     return 0;
 }
@@ -1950,6 +2256,9 @@ static int isabus_fdc_init1(ISADevice *dev)
                           &fdctrl_write_port, fdctrl);
     isa_init_irq(&isa->busdev, &fdctrl->irq, isairq);
 
+    vmstate_register(-1, &vmstate_fdc, fdctrl);
+
+    fdctrl->version = VERSION_INTEL_82078;
     return fdctrl_init_common(fdctrl);
 }
 
@@ -1963,6 +2272,9 @@ static int sysbus_fdc_init1(SysBusDevice *dev)
     sysbus_init_irq(dev, &fdctrl->irq);
     qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
 
+    vmstate_register(-1, &vmstate_fdc, fdctrl);
+
+    fdctrl->version = VERSION_INTEL_82078;
     return fdctrl_init_common(fdctrl);
 }
 
@@ -1977,7 +2289,32 @@ static int sun4m_fdc_init1(SysBusDevice *dev)
     sysbus_init_irq(dev, &fdctrl->irq);
     qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
 
+    vmstate_register(-1, &vmstate_fdc, fdctrl);
+
     fdctrl->sun4m = 1;
+    fdctrl->version = VERSION_INTEL_82078;
+    return fdctrl_init_common(fdctrl);
+}
+
+static int pc98_fdc_init1(ISADevice *dev)
+{
+    static const uint32_t port[8] = {0x90, 0x92, 0x94, 0xbe, 0xc8, 0xca, 0xcc, 
0x4be};
+
+    fdctrl_isabus_t *isa = DO_UPCAST(fdctrl_isabus_t, busdev, dev);
+    fdctrl_t *fdctrl = &isa->state;
+    int i;
+
+    for (i = 0; i < 8; i++) {
+        register_ioport_read(port[i], 1, 1, &pc98_fdctrl_read_port, fdctrl);
+        register_ioport_write(port[i], 1, 1, &pc98_fdctrl_write_port, fdctrl);
+    }
+    isa_init_irq(&isa->busdev, &fdctrl->irq1200, 11);
+    isa_init_irq(&isa->busdev, &fdctrl->irq640, 10);
+
+    vmstate_register(-1, &vmstate_pc98_fdc, fdctrl);
+
+    fdctrl->pc98 = 1;
+    fdctrl->version = VERSION_NEC_UPD765A;
     return fdctrl_init_common(fdctrl);
 }
 
@@ -1999,11 +2336,18 @@ static SysBusDeviceInfo sun4m_fdc_info = {
     .qdev.size  = sizeof(fdctrl_sysbus_t),
 };
 
+static ISADeviceInfo pc98_fdc_info = {
+    .init = pc98_fdc_init1,
+    .qdev.name  = "pc98-fdc",
+    .qdev.size  = sizeof(fdctrl_isabus_t),
+};
+
 static void fdc_register_devices(void)
 {
     isa_qdev_register(&isa_fdc_info);
     sysbus_register_withprop(&sysbus_fdc_info);
     sysbus_register_withprop(&sun4m_fdc_info);
+    isa_qdev_register(&pc98_fdc_info);
 }
 
 device_init(fdc_register_devices)
diff --git a/qemu/hw/fdc.h b/qemu/hw/fdc.h
index 1b81ec1..c6163b9 100644
--- a/qemu/hw/fdc.h
+++ b/qemu/hw/fdc.h
@@ -9,4 +9,5 @@ fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
                              BlockDriverState **fds);
 fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
                              BlockDriverState **fds, qemu_irq *fdc_tc);
+fdctrl_t *pc98_fdctrl_init(BlockDriverState **fds);
 int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);
diff --git a/qemu/hw/hw.h b/qemu/hw/hw.h
index cf266b3..b64710e 100644
--- a/qemu/hw/hw.h
+++ b/qemu/hw/hw.h
@@ -398,6 +398,18 @@ extern const VMStateInfo vmstate_info_buffer;
         + type_check_array(_type,typeof_field(_state, _field),_num)  \
 }
 
+#define VMSTATE_STRUCT_ARRAY_SIZE_UINT8(_field, _state, _field_num, _version, 
_vmsd, _type) { \
+    .name       = (stringify(_field)),                               \
+    .num_offset = offsetof(_state, _field_num)                       \
+        + type_check(uint8_t,typeof_field(_state, _field_num)),      \
+    .version_id = (_version),                                        \
+    .vmsd       = &(_vmsd),                                          \
+    .size       = sizeof(_type),                                     \
+    .flags      = VMS_STRUCT|VMS_ARRAY,                              \
+    .offset     = offsetof(_state, _field)                           \
+            + type_check_array(_type,typeof_field(_state, 
_field),sizeof(typeof_field(_state,_field))/sizeof(_type)) \
+}
+
 #define VMSTATE_STATIC_BUFFER(_field, _state, _version) {            \
     .name       = (stringify(_field)),                               \
     .version_id = (_version),                                        \
@@ -492,6 +504,12 @@ extern const VMStateDescription vmstate_pci_device;
 #define VMSTATE_PTIMER(_f, _s)                                        \
     VMSTATE_PTIMER_V(_f, _s, 0)
 
+#define VMSTATE_UINT8_ARRAY_V(_f, _s, _n, _v)                         \
+    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint8, uint8_t)
+
+#define VMSTATE_UINT8_ARRAY(_f, _s, _n)                               \
+    VMSTATE_UINT8_ARRAY_V(_f, _s, _n, 0)
+
 #define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v)                         \
     VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t)
 





reply via email to

[Prev in Thread] Current Thread [Next in Thread]