[Top][All Lists]
[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)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH v2 03/15] fdc: add PC-9821 family interface,
TAKEDA, toshiya <=