[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 06/14] fdc: support NEC PC-9821 family
From: |
武田 俊也 |
Subject: |
[Qemu-devel] [PATCH 06/14] fdc: support NEC PC-9821 family |
Date: |
Thu, 10 Sep 2009 00:42:07 +0900 |
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
@@ -85,6 +85,7 @@
/* Drive status */
fdrive_type_t drive;
uint8_t perpendicular; /* 2.88 MB access mode */
+ uint8_t seek_result;
/* Position */
uint8_t head;
uint8_t track;
@@ -175,6 +176,7 @@
drv->head = 0;
drv->track = 0;
drv->sect = 1;
+ drv->seek_result = 0;
}
/* Recognize floppy formats */
@@ -387,6 +389,7 @@
};
enum {
+ FD_SR0_NOTRDY = 0x08,
FD_SR0_EQPMT = 0x10,
FD_SR0_SEEK = 0x20,
FD_SR0_ABNTERM = 0x40,
@@ -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];
int reset_sensei;
};
@@ -803,7 +808,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 < 4; i++)
fd_recalibrate(&fdctrl->drives[i]);
fdctrl_reset_fifo(fdctrl);
if (do_irq) {
@@ -825,7 +830,6 @@
return &fdctrl->drives[0];
}
-#if MAX_FD == 4
static inline fdrive_t *drv2 (fdctrl_t *fdctrl)
{
if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
@@ -841,7 +845,6 @@
else
return &fdctrl->drives[2];
}
-#endif
static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
{
@@ -1603,11 +1606,28 @@
static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, int direction)
{
- fdrive_t *cur_drv;
+ fdrive_t *cur_drv = NULL;
- SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
- cur_drv = get_cur_drv(fdctrl);
- fd_recalibrate(cur_drv);
+ if (fdctrl->pc98 && (fdctrl->fifo[1] & 3) >= MAX_FD) {
+ switch (fdctrl->fifo[1] & 3) {
+ case 0: cur_drv = drv0(fdctrl); break;
+ case 1: cur_drv = drv1(fdctrl); break;
+ case 2: cur_drv = drv2(fdctrl); break;
+ case 3: cur_drv = drv3(fdctrl); break;
+ }
+ if (cur_drv != NULL) {
+ cur_drv->seek_result = FD_SR0_ABNTERM | FD_SR0_SEEK |
FD_SR0_NOTRDY;
+ }
+ } else {
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ fd_recalibrate(cur_drv);
+ if (cur_drv->bs != NULL && bdrv_is_inserted(cur_drv->bs)) {
+ cur_drv->seek_result = 0;
+ } else {
+ cur_drv->seek_result = FD_SR0_ABNTERM | FD_SR0_SEEK |
FD_SR0_NOTRDY;
+ }
+ }
fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */
fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
@@ -1615,39 +1635,72 @@
static void fdctrl_handle_sense_interrupt_status (fdctrl_t *fdctrl, int
direction)
{
- 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) {
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (fdctrl->drives[i].seek_result) {
+ fdctrl->fifo[0] = fdctrl->drives[i].seek_result | i;
+ fdctrl->fifo[1] = fdctrl->drives[i].track;
+ fdctrl_set_fifo(fdctrl, 2, 0);
+ fdctrl->drives[i].seek_result = 0;
+ break;
+ }
+ }
+ if (i == 4) {
+ 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);
- }
+ fdrive_t *cur_drv = get_cur_drv(fdctrl);
- fdctrl->fifo[1] = cur_drv->track;
- fdctrl_set_fifo(fdctrl, 2, 0);
- fdctrl_reset_irq(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->status0 = FD_SR0_RDYCHG;
}
static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction)
{
- fdrive_t *cur_drv;
+ fdrive_t *cur_drv = NULL;
- SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
- cur_drv = get_cur_drv(fdctrl);
- fdctrl_reset_fifo(fdctrl);
- if (fdctrl->fifo[2] > cur_drv->max_track) {
- fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
+ if (fdctrl->pc98 && (fdctrl->fifo[1] & 3) >= MAX_FD) {
+ switch (fdctrl->fifo[1] & 3) {
+ case 0: cur_drv = drv0(fdctrl); break;
+ case 1: cur_drv = drv1(fdctrl); break;
+ case 2: cur_drv = drv2(fdctrl); break;
+ case 3: cur_drv = drv3(fdctrl); break;
+ }
+ if (cur_drv != NULL) {
+ cur_drv->seek_result = FD_SR0_ABNTERM | FD_SR0_SEEK |
FD_SR0_NOTRDY;
+ }
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK | FD_SR0_NOTRDY);
} else {
- cur_drv->track = fdctrl->fifo[2];
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl_reset_fifo(fdctrl);
+ if (fdctrl->fifo[2] > cur_drv->max_track) {
+ cur_drv->seek_result = FD_SR0_ABNTERM | FD_SR0_SEEK;
+ } else {
+ cur_drv->track = fdctrl->fifo[2];
+ cur_drv->seek_result = FD_SR0_SEEK;
+ }
/* Raise Interrupt */
- fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+ fdctrl_raise_irq(fdctrl, cur_drv->seek_result);
}
}
@@ -1860,6 +1913,92 @@
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}
+/* NEC PC-98x1 */
+
+/*
+ MEMO: Use fdctrl->pwrd to store access mode flag:
+
+ bit0-3: 1.44MB access mode of drive #0-#3
+ bit5-6: drive select of bit0-3
+ bit7 : 1MB/640KB access mode
+*/
+
+static uint32_t pc98_fdctrl_read_port (void *opaque, uint32_t reg)
+{
+ fdctrl_t *fdctrl = opaque;
+ int bit;
+
+ switch (reg) {
+ case 0x90:
+ return fdctrl_read_main_status(fdctrl);
+ case 0x92:
+ return fdctrl_read_data(fdctrl);
+ case 0x94:
+ return 0x44;
+ case 0xbe:
+ if (fdctrl->pwrd & 0x80) {
+ return 0xff;
+ } else {
+ return 0xfd;
+ }
+ case 0x4be:
+ bit = 1 << ((fdctrl->pwrd >> 5) & 3);
+ if (fdctrl->pwrd & bit) {
+ return 0xff;
+ } else {
+ return 0xfe;
+ }
+ }
+ return 0xff;
+}
+
+static void pc98_fdctrl_write_port (void *opaque, uint32_t reg, uint32_t value)
+{
+ fdctrl_t *fdctrl = opaque;
+
+ switch (reg) {
+ case 0x90:
+ /* read-only */
+ break;
+ case 0x92:
+ fdctrl_write_data(fdctrl, value);
+ break;
+ case 0x94:
+ /* Reset */
+ if (!(value & 0x80)) {
+ if (fdctrl->dor & 0x80) {
+ FLOPPY_DPRINTF("controller enter RESET state\n");
+ }
+ } else {
+ if (!(fdctrl->dor & 0x80)) {
+ FLOPPY_DPRINTF("controller out of RESET state\n");
+ fdctrl_reset(fdctrl, 1);
+ fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+ }
+ }
+ fdctrl->dor = value;
+ break;
+ case 0xbe:
+ if (value & 2) {
+ fdctrl->pwrd |= 0x80;
+ } else {
+ fdctrl->pwrd &= ~0x80;
+ }
+ break;
+ case 0x4be:
+ if (value & 0x10) {
+ int bit = 1 << ((value >> 5) & 3);
+ if (value & 1) {
+ fdctrl->pwrd |= bit;
+ } else {
+ fdctrl->pwrd &= ~bit;
+ }
+ }
+ fdctrl->pwrd = (fdctrl->pwrd & ~0x60) | (value & 0x60);
+ break;
+ }
+}
+
/* Init functions */
static void fdctrl_connect_drives(fdctrl_t *fdctrl, BlockDriverState **fds)
{
@@ -1933,6 +2072,24 @@
return fdctrl;
}
+fdctrl_t *pc98_fdctrl_init(int isairq, int dma_chann,
+ uint32_t io_base,
+ BlockDriverState **fds)
+{
+ fdctrl_t *fdctrl;
+ ISADevice *dev;
+
+ dev = isa_create_simple("pc98-fdc", io_base, 0, isairq, -1);
+ fdctrl = &(DO_UPCAST(fdctrl_isabus_t, busdev, dev)->state);
+
+ fdctrl->dma_chann = dma_chann;
+ DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
+
+ fdctrl_connect_drives(fdctrl, fds);
+
+ return fdctrl;
+}
+
static int fdctrl_init_common(fdctrl_t *fdctrl)
{
int i, j;
@@ -2010,6 +2167,27 @@
return fdctrl_init_common(fdctrl);
}
+static const int pc98_io_base[5] = {0x90, 0x92, 0x94, 0xbe, 0x4be};
+
+static int pc98_fdc_init1(ISADevice *dev)
+{
+ fdctrl_isabus_t *isa = DO_UPCAST(fdctrl_isabus_t, busdev, dev);
+ fdctrl_t *fdctrl = &isa->state;
+ int i;
+ uint32_t io_base = pc98_io_base[0];
+
+ for (i = 0; i < 5; i++) {
+ register_ioport_read(isa->busdev.iobase[0] + pc98_io_base[i] -
io_base, 1, 1,
+ &pc98_fdctrl_read_port, fdctrl);
+ register_ioport_write(isa->busdev.iobase[0] + pc98_io_base[i] -
io_base, 1, 1,
+ &pc98_fdctrl_write_port, fdctrl);
+ }
+ isa_init_irq(&isa->busdev, &fdctrl->irq);
+
+ fdctrl->pc98 = 1;
+ return fdctrl_init_common(fdctrl);
+}
+
static ISADeviceInfo isa_fdc_info = {
.init = isabus_fdc_init1,
.qdev.name = "isa-fdc",
@@ -2028,11 +2206,18 @@
.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 -ur a/hw/fdc.h b/hw/fdc.h
--- a/hw/fdc.h Tue Sep 8 21:26:50 2009
+++ b/hw/fdc.h Wed Sep 9 21:51:31 2009
@@ -11,4 +11,7 @@
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(int isairq, int dma_chann,
+ uint32_t io_base,
+ BlockDriverState **fds);
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);
- [Qemu-devel] [PATCH 06/14] fdc: support NEC PC-9821 family,
武田 俊也 <=