diff mbox

Re: fdc: support NEC PC-9821 family

Message ID 200909111136.AA00103@YOUR-BD18D6DD63.m1.interq.or.jp
State Superseded
Headers show

Commit Message

武田 =?ISO-2022-JP?B?IBskQj1TTGkbKEI=?= Sept. 11, 2009, 11:36 a.m. UTC
Dear Juan,

>t-takeda@m1.interq.or.jp (武田 俊也) wrote:
>> Dear Alex,
>>
>>>On 09.09.2009, at 17:42, 武田 俊也 wrote:
>>>
>>>> This patch is to add NEC PC-9821 family i/o to fdc.
>>>>
>>>> diff -ur a/hw/fdc.c b/hw/fdc.c
>>>> --- a/hw/fdc.c	Tue Sep  8 21:26:50 2009
>>>> +++ b/hw/fdc.c	Wed Sep  9 21:51:23 2009
	[snip]
>>>> @@ -507,8 +510,10 @@
>>>>     uint8_t pwrd;
>>>>     /* Sun4m quirks? */
>>>>     int sun4m;
>>>> +    /* NEC PC-98x1 quirks? */
>>>> +    int pc98;
>>>>     /* Floppy drives */
>>>> -    fdrive_t drives[MAX_FD];
>>>> +    fdrive_t drives[4];
>>>
>>>Just change MAX_FD?
>
>Could you just change MAX_FD = 4
>remove your MAX_FD_BUFFER
>and change all remaining users of MAX_FD to 2?
>The only places where MAX_FD is used now is in the init functions.

I modified the patch again.

MAX_FD_BUFFER is removed and MAX_FD is changed to the local definition.

In each machine, MAX_FD is defined for its machine's floppy drive number.
In fdc.c the floppy drive number is given by param of _init() function, 
so each machine can specify its own drive number.

>>      qemu_get_8s(f, &s->srb);
>>      qemu_get_8s(f, &s->dor);
	[snip]
>> @@ -718,8 +710,9 @@
>>      qemu_get_8s(f, &s->pwrd);
>>      qemu_get_8s(f, &n);
>>  
>> -    if (n > MAX_FD)
>> +    if (n > MAX_FD_BUFFER)
>>          return -EINVAL;
>> +    s->max_fd = n;
>
>You also need to check that is was the same than before.
>If you boot with 2 fd's and after load you get 4, things are going to be
>funny.

I modified to check the loaded drive number n == s->max_fd.

This is new patch.

Comments

Stuart Brady Sept. 11, 2009, 8:17 p.m. UTC | #1
On Fri, Sep 11, 2009 at 08:36:02PM +0900, 武田 俊也 wrote:
> This is new patch.

Nice work -- looks a lot closer! :)

I've a few questions, though.

I notice that the 82078 has 'PC AT', 'PS/2' and 'PS/2 Model 30' modes.
QEMU doesn't seem to implement this, but I don't know common each of the
different modes is/was.

Also, I notice that in fdctrl_write_dor(), the DR0 bit (in PS/2 mode)
is set if drives 0 or 2 are selected, which looks like a bug -- I would
expect that it should only be set if drive 0 is selected.

AFAIK, in most IBM PC 'clones' (but not quite all), the FDC can support
four drives, but only up to one/two drives can be physically attached.

It seems that MAX_FD affects emulation of the FDC itself, and doesn't
just determine the number of drives that may be connected.  Surely that
can't be right?  I.e. if MAX_FD == 2, any attempt to select drive 2
results in drive 0 being selected, which should not be the case...

I'm not sure why QEMU limits certain machines to two drives...  Maybe
the reason is that software for systems where only two drives can be
physically connected might react badly to seeing unexpected drives.
Is this the case?  TBH, I'm not sure whether I *really* need more than
a single drive... :)

Also, it seems to me that a floppy controller might have either one,
two, three, four or no drives attached.  (I assume that software can
distinguish between a missing drive and a drive with no disk inserted.)
In that case, it seems to me that QEMU cannot provide emulation of a
system with no drives connected to its FDC.

Cheers,
武田 =?ISO-2022-JP?B?IBskQj1TTGkbKEI=?= Sept. 12, 2009, 4:02 a.m. UTC | #2
Dear Stuart and members,

>On Fri, Sep 11, 2009 at 08:36:02PM +0900, 武田 俊也 wrote:
>> This is new patch.
>
>Nice work -- looks a lot closer! :)
>
>I've a few questions, though.
>
>I notice that the 82078 has 'PC AT', 'PS/2' and 'PS/2 Model 30' modes.
>QEMU doesn't seem to implement this, but I don't know common each of the
>different modes is/was.
>
>Also, I notice that in fdctrl_write_dor(), the DR0 bit (in PS/2 mode)
>is set if drives 0 or 2 are selected, which looks like a bug -- I would
>expect that it should only be set if drive 0 is selected.
>
>AFAIK, in most IBM PC 'clones' (but not quite all), the FDC can support
>four drives, but only up to one/two drives can be physically attached.
>
>It seems that MAX_FD affects emulation of the FDC itself, and doesn't
>just determine the number of drives that may be connected.  Surely that
>can't be right?  I.e. if MAX_FD == 2, any attempt to select drive 2
>results in drive 0 being selected, which should not be the case...
>
>I'm not sure why QEMU limits certain machines to two drives...  Maybe
>the reason is that software for systems where only two drives can be
>physically connected might react badly to seeing unexpected drives.
>Is this the case?  TBH, I'm not sure whether I *really* need more than
>a single drive... :)
>
>Also, it seems to me that a floppy controller might have either one,
>two, three, four or no drives attached.  (I assume that software can
>distinguish between a missing drive and a drive with no disk inserted.)
>In that case, it seems to me that QEMU cannot provide emulation of a
>system with no drives connected to its FDC.
>
>Cheers,
>-- 
>Stuart Brady

I agree.

In PC-98x1 family case (uPD765A is used for fdc), bios checks which drive
the disket is inserted to, by the commands below:
	recalib to drive0
	recalib to drive1
	recalib to drive2
	recalib to drive3
	sence interrupt stat
Yes, if MAX_FD = 2, it is recognized the same drive is connected to
drive0 and drive2, drive1 and drive3.
So I need to change MAX_FD to 4 for PC-98x1 family.

I think we need to divide MAX_FD for 2 meanings,
the number of drives that floppy drive controller can control, is always 4,
and the number of drives that each machine has, is 2 or 4.
For example, MAX_FD used in FD_DOR_SELMASK definition is the former.
MAX_FD in save/load function is the latter.

(Sorry I don't enough about 82078, so I cannot comment about PC/AT and
other mode.)

Thanks,
TAKEDA, toshiya
Natalia Portillo Sept. 15, 2009, 12:28 p.m. UTC | #3
I can assure you all the Shugart based controlers (NEC FD765 and  
derived like the Intel 82078) do support four drives per controller.

El 12/09/2009, a las 5:02, 武田 俊也 escribió:

> Dear Stuart and members,
>
>> On Fri, Sep 11, 2009 at 08:36:02PM +0900, 武田 俊也 wrote:
>>> This is new patch.
>>
>> Nice work -- looks a lot closer! :)
>>
>> I've a few questions, though.
>>
>> I notice that the 82078 has 'PC AT', 'PS/2' and 'PS/2 Model 30'  
>> modes.
>> QEMU doesn't seem to implement this, but I don't know common each  
>> of the
>> different modes is/was.
>>
>> Also, I notice that in fdctrl_write_dor(), the DR0 bit (in PS/2 mode)
>> is set if drives 0 or 2 are selected, which looks like a bug -- I  
>> would
>> expect that it should only be set if drive 0 is selected.
>>
>> AFAIK, in most IBM PC 'clones' (but not quite all), the FDC can  
>> support
>> four drives, but only up to one/two drives can be physically  
>> attached.
>>
>> It seems that MAX_FD affects emulation of the FDC itself, and doesn't
>> just determine the number of drives that may be connected.  Surely  
>> that
>> can't be right?  I.e. if MAX_FD == 2, any attempt to select drive 2
>> results in drive 0 being selected, which should not be the case...
>>
>> I'm not sure why QEMU limits certain machines to two drives...  Maybe
>> the reason is that software for systems where only two drives can be
>> physically connected might react badly to seeing unexpected drives.
>> Is this the case?  TBH, I'm not sure whether I *really* need more  
>> than
>> a single drive... :)
>>
>> Also, it seems to me that a floppy controller might have either one,
>> two, three, four or no drives attached.  (I assume that software can
>> distinguish between a missing drive and a drive with no disk  
>> inserted.)
>> In that case, it seems to me that QEMU cannot provide emulation of a
>> system with no drives connected to its FDC.
>>
>> Cheers,
>> -- 
>> Stuart Brady
>
> I agree.
>
> In PC-98x1 family case (uPD765A is used for fdc), bios checks which  
> drive
> the disket is inserted to, by the commands below:
> 	recalib to drive0
> 	recalib to drive1
> 	recalib to drive2
> 	recalib to drive3
> 	sence interrupt stat
> Yes, if MAX_FD = 2, it is recognized the same drive is connected to
> drive0 and drive2, drive1 and drive3.
> So I need to change MAX_FD to 4 for PC-98x1 family.
>
> I think we need to divide MAX_FD for 2 meanings,
> the number of drives that floppy drive controller can control, is  
> always 4,
> and the number of drives that each machine has, is 2 or 4.
> For example, MAX_FD used in FD_DOR_SELMASK definition is the former.
> MAX_FD in save/load function is the latter.
>
> (Sorry I don't enough about 82078, so I cannot comment about PC/AT and
> other mode.)
>
> Thanks,
> TAKEDA, toshiya
>
>
>
>
diff mbox

Patch

diff -ur a/hw/fdc.c b/hw/fdc.c
--- a/hw/fdc.c	Thu Sep 10 22:27:14 2009
+++ b/hw/fdc.c	Fri Sep 11 18:40:22 2009
@@ -52,6 +52,8 @@ 
 /********************************************************/
 /* Floppy drive emulation                               */
 
+#define MAX_FD 4
+
 #define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
 #define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
 
@@ -424,11 +426,6 @@ 
 };
 
 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,
@@ -437,13 +434,8 @@ 
     FD_DOR_MOTEN3   = 0x80,
 };
 
-enum {
-#if MAX_FD == 4
-    FD_TDR_BOOTSEL  = 0x0c,
-#else
-    FD_TDR_BOOTSEL  = 0x04,
-#endif
-};
+#define FD_DOR_SELMASK(fdctrl) (fdctrl->max_fd - 1)
+#define FD_TDR_BOOTSEL(fdctrl) ((fdctrl->max_fd - 1) << 2)
 
 enum {
     FD_DSR_DRATEMASK= 0x03,
@@ -473,6 +465,8 @@ 
 struct fdctrl_t {
     /* Controller's identification */
     uint8_t version;
+    /* Max drives (must be 2 or 4) */
+    uint8_t max_fd;
     /* HW */
     qemu_irq irq;
     int dma_chann;
@@ -637,7 +631,6 @@ 
 static void fdc_save (QEMUFile *f, void *opaque)
 {
     fdctrl_t *s = opaque;
-    uint8_t tmp;
     int i;
     uint8_t dor = s->dor | GET_CUR_DRV(s);
 
@@ -666,9 +659,8 @@ 
     qemu_put_8s(f, &s->lock);
     qemu_put_8s(f, &s->pwrd);
 
-    tmp = MAX_FD;
-    qemu_put_8s(f, &tmp);
-    for (i = 0; i < MAX_FD; i++)
+    qemu_put_8s(f, &s->max_fd);
+    for (i = 0; i < s->max_fd; i++)
         fd_save(f, &s->drives[i]);
 }
 
@@ -694,8 +686,8 @@ 
     qemu_get_8s(f, &s->sra);
     qemu_get_8s(f, &s->srb);
     qemu_get_8s(f, &s->dor);
-    SET_CUR_DRV(s, s->dor & FD_DOR_SELMASK);
-    s->dor &= ~FD_DOR_SELMASK;
+    SET_CUR_DRV(s, s->dor & FD_DOR_SELMASK(s));
+    s->dor &= ~FD_DOR_SELMASK(s);
     qemu_get_8s(f, &s->tdr);
     qemu_get_8s(f, &s->dsr);
     qemu_get_8s(f, &s->msr);
@@ -718,10 +710,10 @@ 
     qemu_get_8s(f, &s->pwrd);
     qemu_get_8s(f, &n);
 
-    if (n > MAX_FD)
+    if (n > MAX_FD || n != s->max_fd)
         return -EINVAL;
 
-    for (i = 0; i < n; i++) {
+    for (i = 0; i < s->max_fd; i++) {
         ret = fd_load(f, &s->drives[i]);
         if (ret != 0)
             break;
@@ -803,7 +795,7 @@ 
     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 < fdctrl->max_fd; i++)
         fd_recalibrate(&fdctrl->drives[i]);
     fdctrl_reset_fifo(fdctrl);
     if (do_irq) {
@@ -814,12 +806,12 @@ 
 
 static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
 {
-    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
+    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL(fdctrl)) >> 2];
 }
 
 static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
 {
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
+    if ((fdctrl->tdr & FD_TDR_BOOTSEL(fdctrl)) < (1 << 2))
         return &fdctrl->drives[1];
     else
         return &fdctrl->drives[0];
@@ -828,7 +820,9 @@ 
 #if MAX_FD == 4
 static inline fdrive_t *drv2 (fdctrl_t *fdctrl)
 {
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
+    if (fdctrl->max_fd != 4)
+        return NULL;
+    else if ((fdctrl->tdr & FD_TDR_BOOTSEL(fdctrl)) < (2 << 2))
         return &fdctrl->drives[2];
     else
         return &fdctrl->drives[1];
@@ -836,7 +830,9 @@ 
 
 static inline fdrive_t *drv3 (fdctrl_t *fdctrl)
 {
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
+    if (fdctrl->max_fd != 4)
+        return NULL;
+    else if ((fdctrl->tdr & FD_TDR_BOOTSEL(fdctrl)) < (3 << 2))
         return &fdctrl->drives[3];
     else
         return &fdctrl->drives[2];
@@ -921,7 +917,7 @@ 
         }
     }
     /* Selected drive */
-    fdctrl->cur_drv = value & FD_DOR_SELMASK;
+    fdctrl->cur_drv = value & FD_DOR_SELMASK(fdctrl);
 
     fdctrl->dor = value;
 }
@@ -945,7 +941,7 @@ 
     }
     FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
     /* Disk boot selection indicator */
-    fdctrl->tdr = value & FD_TDR_BOOTSEL;
+    fdctrl->tdr = value & FD_TDR_BOOTSEL(fdctrl);
     /* Tape indicators: never allow */
 }
 
@@ -1004,8 +1000,8 @@ 
     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))
+     || (fdctrl->max_fd == 4 && fdctrl_media_changed(drv2(fdctrl)))
+     || (fdctrl->max_fd == 4 && fdctrl_media_changed(drv3(fdctrl)))
 #endif
         )
         retval |= FD_DIR_DSKCHG;
@@ -1109,7 +1105,7 @@ 
     uint8_t kh, kt, ks;
     int did_seek = 0;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     kt = fdctrl->fifo[2];
     kh = fdctrl->fifo[3];
@@ -1379,7 +1375,7 @@ 
     fdrive_t *cur_drv;
     uint8_t kh, kt, ks;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     kt = fdctrl->fifo[6];
     kh = fdctrl->fifo[7];
@@ -1451,11 +1447,15 @@ 
     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;
+    if (fdctrl->max_fd == 4) {
+        fdctrl->fifo[2] = drv2(fdctrl)->track;
+        fdctrl->fifo[3] = drv3(fdctrl)->track;
+    } else
 #else
-    fdctrl->fifo[2] = 0;
-    fdctrl->fifo[3] = 0;
+    {
+        fdctrl->fifo[2] = 0;
+        fdctrl->fifo[3] = 0;
+    }
 #endif
     /* timers */
     fdctrl->fifo[4] = fdctrl->timer0;
@@ -1489,8 +1489,10 @@ 
     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];
+    if (fdctrl->max_fd == 4) {
+        drv2(fdctrl)->track = fdctrl->fifo[5];
+        drv3(fdctrl)->track = fdctrl->fifo[6];
+    }
 #endif
     /* timers */
     fdctrl->timer0 = fdctrl->fifo[7];
@@ -1514,11 +1516,15 @@ 
     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;
+    if (fdctrl->max_fd == 4) {
+        fdctrl->fifo[4] = drv2(fdctrl)->track;
+        fdctrl->fifo[5] = drv3(fdctrl)->track;
+    } else
 #else
-    fdctrl->fifo[4] = 0;
-    fdctrl->fifo[5] = 0;
+    {
+        fdctrl->fifo[4] = 0;
+        fdctrl->fifo[5] = 0;
+    }
 #endif
     /* timers */
     fdctrl->fifo[6] = fdctrl->timer0;
@@ -1548,7 +1554,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     fdctrl->data_state |= FD_STATE_FORMAT;
     if (fdctrl->fifo[0] & 0x80)
@@ -1589,7 +1595,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
     /* 1 Byte status back */
@@ -1605,7 +1611,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     fd_recalibrate(cur_drv);
     fdctrl_reset_fifo(fdctrl);
@@ -1639,7 +1645,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     fdctrl_reset_fifo(fdctrl);
     if (fdctrl->fifo[2] > cur_drv->max_track) {
@@ -1708,7 +1714,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     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;
@@ -1724,7 +1730,7 @@ 
 {
     fdrive_t *cur_drv;
 
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK(fdctrl));
     cur_drv = get_cur_drv(fdctrl);
     if (fdctrl->fifo[2] > cur_drv->track) {
         cur_drv->track = 0;
@@ -1865,7 +1871,7 @@ 
 {
     unsigned int i;
 
-    for (i = 0; i < MAX_FD; i++) {
+    for (i = 0; i < fdctrl->max_fd; i++) {
         fd_init(&fdctrl->drives[i], fds[i]);
         fd_revalidate(&fdctrl->drives[i]);
     }
@@ -1873,15 +1879,21 @@ 
 
 fdctrl_t *fdctrl_init_isa(int isairq, int dma_chann,
                           uint32_t io_base,
-                          BlockDriverState **fds)
+                          BlockDriverState **fds, uint8_t max_fd)
 {
     fdctrl_t *fdctrl;
     ISADevice *dev;
 
+    if (max_fd > MAX_FD) {
+        hw_error("fdctrl_init_isa: invalid max_fd");
+    }
+
     dev = isa_create_simple("isa-fdc", io_base, 0, isairq, -1);
     fdctrl = &(DO_UPCAST(fdctrl_isabus_t, busdev, dev)->state);
 
     fdctrl->dma_chann = dma_chann;
+    fdctrl->max_fd = max_fd;
+
     DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
 
     fdctrl_connect_drives(fdctrl, fds);
@@ -1891,12 +1903,16 @@ 
 
 fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
                              target_phys_addr_t mmio_base,
-                             BlockDriverState **fds)
+                             BlockDriverState **fds, uint8_t max_fd)
 {
     fdctrl_t *fdctrl;
     DeviceState *dev;
     fdctrl_sysbus_t *sys;
 
+    if (max_fd > MAX_FD) {
+        hw_error("fdctrl_init_sysbus: invalid max_fd");
+    }
+
     dev = qdev_create(NULL, "sysbus-fdc");
     qdev_init(dev);
     sys = DO_UPCAST(fdctrl_sysbus_t, busdev.qdev, dev);
@@ -1905,6 +1921,8 @@ 
     sysbus_mmio_map(&sys->busdev, 0, mmio_base);
 
     fdctrl->dma_chann = dma_chann;
+    fdctrl->max_fd = max_fd;
+
     DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
     fdctrl_connect_drives(fdctrl, fds);
 
@@ -1912,12 +1930,17 @@ 
 }
 
 fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
-                             BlockDriverState **fds, qemu_irq *fdc_tc)
+                             BlockDriverState **fds, uint8_t max_fd,
+                             qemu_irq *fdc_tc)
 {
     DeviceState *dev;
     fdctrl_sysbus_t *sys;
     fdctrl_t *fdctrl;
 
+    if (max_fd > MAX_FD) {
+        hw_error("sun4m_fdctrl_init: invalid max_fd");
+    }
+
     dev = qdev_create(NULL, "SUNW,fdtwo");
     qdev_init(dev);
     sys = DO_UPCAST(fdctrl_sysbus_t, busdev.qdev, dev);
@@ -1927,6 +1950,7 @@ 
     *fdc_tc = qdev_get_gpio_in(dev, 0);
 
     fdctrl->dma_chann = -1;
+    fdctrl->max_fd = max_fd;
 
     fdctrl_connect_drives(fdctrl, fds);
 
diff -ur a/hw/fdc.h b/hw/fdc.h
--- a/hw/fdc.h	Thu Sep 10 22:27:14 2009
+++ b/hw/fdc.h	Fri Sep 11 18:19:56 2009
@@ -1,14 +1,13 @@ 
 /* fdc.c */
-#define MAX_FD 2
-
 typedef struct fdctrl_t fdctrl_t;
 
 fdctrl_t *fdctrl_init_isa(int isairq, int dma_chann,
                           uint32_t io_base,
-                          BlockDriverState **fds);
+                          BlockDriverState **fds, uint8_t max_fd);
 fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
                              target_phys_addr_t mmio_base,
-                             BlockDriverState **fds);
+                             BlockDriverState **fds, uint8_t max_fd);
 fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
-                             BlockDriverState **fds, qemu_irq *fdc_tc);
+                             BlockDriverState **fds, uint8_t max_fd,
+                             qemu_irq *fdc_tc);
 int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);
diff -ur a/hw/mips_jazz.c b/hw/mips_jazz.c
--- a/hw/mips_jazz.c	Thu Sep 10 22:27:14 2009
+++ b/hw/mips_jazz.c	Fri Sep 11 18:49:52 2009
@@ -113,6 +113,8 @@ 
 #define MAGNUM_BIOS_SIZE_MAX 0x7e000
 #define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX)
 
+#define MAX_FD 2
+
 static
 void mips_jazz_init (ram_addr_t ram_size,
                      const char *cpu_model,
@@ -238,7 +240,7 @@ 
         DriveInfo *dinfo = drive_get(IF_FLOPPY, 0, n);
         fds[n] = dinfo ? dinfo->bdrv : NULL;
     }
-    fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds);
+    fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds, MAX_FD);
 
     /* Real time clock */
     rtc_init(0x70, i8259[8], 1980);
diff -ur a/hw/mips_malta.c b/hw/mips_malta.c
--- a/hw/mips_malta.c	Thu Sep 10 22:27:14 2009
+++ b/hw/mips_malta.c	Fri Sep 11 18:48:16 2009
@@ -55,6 +55,7 @@ 
 #define ENVP_ENTRY_SIZE	 	256
 
 #define MAX_IDE_BUS 2
+#define MAX_FD 2
 
 typedef struct {
     uint32_t leds;
@@ -932,7 +933,7 @@ 
         dinfo = drive_get(IF_FLOPPY, 0, i);
         fd[i] = dinfo ? dinfo->bdrv : NULL;
     }
-    floppy_controller = fdctrl_init_isa(6, 2, 0x3f0, fd);
+    floppy_controller = fdctrl_init_isa(6, 2, 0x3f0, fd, MAX_FD);
 
     /* Sound card */
 #ifdef HAS_AUDIO
diff -ur a/hw/pc.c b/hw/pc.c
--- a/hw/pc.c	Thu Sep 10 22:27:14 2009
+++ b/hw/pc.c	Fri Sep 11 18:24:52 2009
@@ -58,6 +58,7 @@ 
 #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
 
 #define MAX_IDE_BUS 2
+#define MAX_FD 2
 
 static fdctrl_t *floppy_controller;
 static RTCState *rtc_state;
@@ -1382,7 +1383,7 @@ 
         dinfo = drive_get(IF_FLOPPY, 0, i);
         fd[i] = dinfo ? dinfo->bdrv : NULL;
     }
-    floppy_controller = fdctrl_init_isa(6, 2, 0x3f0, fd);
+    floppy_controller = fdctrl_init_isa(6, 2, 0x3f0, fd, MAX_FD);
 
     cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd);
 
diff -ur a/hw/ppc_prep.c b/hw/ppc_prep.c
--- a/hw/ppc_prep.c	Thu Sep 10 22:27:14 2009
+++ b/hw/ppc_prep.c	Fri Sep 11 18:46:44 2009
@@ -41,6 +41,7 @@ 
 #define MAX_CPUS 1
 
 #define MAX_IDE_BUS 2
+#define MAX_FD 2
 
 #define BIOS_SIZE (1024 * 1024)
 #define BIOS_FILENAME "ppc_rom.bin"
@@ -719,7 +720,7 @@ 
         dinfo = drive_get(IF_FLOPPY, 0, i);
         fd[i] = dinfo ? dinfo->bdrv : NULL;
     }
-    fdctrl_init_isa(6, 2, 0x3f0, fd);
+    fdctrl_init_isa(6, 2, 0x3f0, fd, MAX_FD);
 
     /* Register speaker port */
     register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL);
diff -ur a/hw/sun4m.c b/hw/sun4m.c
--- a/hw/sun4m.c	Thu Sep 10 22:27:14 2009
+++ b/hw/sun4m.c	Fri Sep 11 18:45:18 2009
@@ -81,6 +81,7 @@ 
 #define PROM_SIZE_MAX        (1024 * 1024)
 #define PROM_VADDR           0xffd00000
 #define PROM_FILENAME        "openbios-sparc32"
+#define MAX_FD               2
 #define CFG_ADDR             0xd00000510ULL
 #define FW_CFG_SUN4M_DEPTH   (FW_CFG_ARCH_LOCAL + 0x00)
 
@@ -827,7 +828,7 @@ 
         if (dinfo)
             fd[0] = dinfo->bdrv;
 
-        sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd,
+        sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd, MAX_FD,
                           &fdc_tc);
     }
 
@@ -1611,7 +1612,7 @@ 
         if (dinfo)
             fd[0] = dinfo->bdrv;
 
-        sun4m_fdctrl_init(slavio_irq[1], hwdef->fd_base, fd,
+        sun4m_fdctrl_init(slavio_irq[1], hwdef->fd_base, fd, MAX_FD,
                           &fdc_tc);
     }
 
diff -ur a/hw/sun4u.c b/hw/sun4u.c
--- a/hw/sun4u.c	Thu Sep 10 22:27:14 2009
+++ b/hw/sun4u.c	Fri Sep 11 18:42:18 2009
@@ -55,6 +55,7 @@ 
 #define PROM_FILENAME        "openbios-sparc64"
 #define NVRAM_SIZE           0x2000
 #define MAX_IDE_BUS          2
+#define MAX_FD               2
 #define BIOS_CFG_IOPORT      0x510
 #define FW_CFG_SPARC64_WIDTH (FW_CFG_ARCH_LOCAL + 0x00)
 #define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
@@ -622,7 +623,7 @@ 
         dinfo = drive_get(IF_FLOPPY, 0, i);
         fd[i] = dinfo ? dinfo->bdrv : NULL;
     }
-    fdctrl_init_isa(6, 2, 0x3f0, fd);
+    fdctrl_init_isa(6, 2, 0x3f0, fd, MAX_FD);
     /* FIXME: wire up interrupts.  */
     nvram = m48t59_init(NULL/*8*/, 0, 0x0074, NVRAM_SIZE, 59);