@@ -465,14 +465,16 @@ extern const VMStateInfo vmstate_info_unused_buffer;
.offset = vmstate_offset_array(_state, _field, _type, _num), \
}
-#define VMSTATE_STRUCT_ARRAY_SIZE_UINT8(_field, _state, _field__num, _version, _vmsd, _type) { \
+#define VMSTATE_STRUCT_ARRAY_SIZE_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \
.name = (stringify(_field)), \
- .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
+ .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 = vmstate_offset_array(_state, _field, _type, _num), \
+ .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, _test, _start, _size) { \
@@ -57,6 +57,7 @@
/* Will always be a fixed parameter for us */
#define FD_SECTOR_LEN 512
+#define FD_SECTOR_LEN_2 1024
#define FD_SECTOR_SC 2 /* Sector size code */
#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */
@@ -95,7 +96,10 @@ typedef struct fdrive_t {
uint8_t last_sect; /* Nb sector per track */
uint8_t max_track; /* Nb of tracks */
uint16_t bps; /* Bytes per sector */
+ int sect_mul; /* Bytes per sector / 512 */
uint8_t ro; /* Is read-only */
+ /* NEC PC-9821 */
+ uint8_t status0;
} fdrive_t;
static void fd_init (fdrive_t *drv)
@@ -107,6 +111,8 @@ static void fd_init (fdrive_t *drv)
/* Disk */
drv->last_sect = 0;
drv->max_track = 0;
+ drv->bps = FD_SECTOR_LEN;
+ drv->sect_mul = 1;
}
static int _fd_sector (uint8_t head, uint8_t track,
@@ -176,12 +182,14 @@ static void fd_recalibrate (fdrive_t *drv)
drv->head = 0;
drv->track = 0;
drv->sect = 1;
+ drv->status0 = 0;
}
/* Recognize floppy formats */
typedef struct fd_format_t {
fdrive_type_t drive;
fdisk_type_t disk;
+ uint16_t bps;
uint8_t last_sect;
uint8_t max_track;
uint8_t max_head;
@@ -191,48 +199,52 @@ typedef struct fd_format_t {
static const fd_format_t fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 18, 80, 1, "1.44 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 20, 80, 1, "1.6 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 21, 80, 1, "1.68 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 21, 82, 1, "1.72 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 21, 83, 1, "1.74 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 22, 80, 1, "1.76 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 23, 80, 1, "1.84 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 512, 24, 80, 1, "1.92 MB 3\"1/2", },
/* 2.88 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
- { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
- { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", },
- { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
- { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 512, 36, 80, 1, "2.88 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 512, 39, 80, 1, "3.12 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 512, 40, 80, 1, "3.2 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 512, 44, 80, 1, "3.52 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 512, 48, 80, 1, "3.84 MB 3\"1/2", },
/* 720 kB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 9, 80, 1, "720 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 10, 80, 1, "800 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 10, 82, 1, "820 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 10, 83, 1, "830 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 13, 80, 1, "1.04 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 14, 80, 1, "1.12 MB 3\"1/2", },
+ /* 1.23 MB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 1024, 8, 77, 1, "1.23 MB 5\"1/4", },
/* 1.2 MB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 15, 80, 1, "1.2 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 18, 80, 1, "1.44 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 18, 82, 1, "1.48 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 18, 83, 1, "1.49 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 20, 80, 1, "1.6 MB 5\"1/4", },
/* 720 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 9, 80, 1, "720 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 11, 80, 1, "880 kB 5\"1/4", },
+ /* 640 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 8, 80, 1, "640 kB 5\"1/4", },
/* 360 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 9, 40, 1, "360 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 9, 40, 0, "180 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 10, 41, 1, "410 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 10, 42, 1, "420 kB 5\"1/4", },
/* 320 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", },
- { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 8, 40, 1, "320 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 512, 8, 40, 0, "160 kB 5\"1/4", },
/* 360 kB must match 5"1/4 better than 3"1/2... */
- { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 512, 9, 80, 0, "360 kB 3\"1/2", },
/* end */
- { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
+ { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, -1, 0, NULL, },
};
/* Revalidate a disk drive after a disk change */
@@ -242,6 +254,7 @@ static void fd_revalidate (fdrive_t *drv)
uint64_t nb_sectors, size;
int i, first_match, match;
int nb_heads, max_track, last_sect, ro;
+ int bps = FD_SECTOR_LEN;
FLOPPY_DPRINTF("revalidate\n");
if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
@@ -261,7 +274,7 @@ static void fd_revalidate (fdrive_t *drv)
if (drv->drive == parse->drive ||
drv->drive == FDRIVE_DRV_NONE) {
size = (parse->max_head + 1) * parse->max_track *
- parse->last_sect;
+ parse->last_sect * (parse->bps / FD_SECTOR_LEN);
if (nb_sectors == size) {
match = i;
break;
@@ -280,6 +293,7 @@ static void fd_revalidate (fdrive_t *drv)
nb_heads = parse->max_head + 1;
max_track = parse->max_track;
last_sect = parse->last_sect;
+ bps = parse->bps;
drv->drive = parse->drive;
FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
nb_heads, max_track, last_sect, ro ? "ro" : "rw");
@@ -291,11 +305,15 @@ static void fd_revalidate (fdrive_t *drv)
}
drv->max_track = max_track;
drv->last_sect = last_sect;
+ drv->bps = bps;
+ drv->sect_mul = bps / FD_SECTOR_LEN;
drv->ro = ro;
} else {
FLOPPY_DPRINTF("No disk in drive\n");
drv->last_sect = 0;
drv->max_track = 0;
+ drv->bps = FD_SECTOR_LEN;
+ drv->sect_mul = 1;
drv->flags &= ~FDISK_DBL_SIDES;
}
}
@@ -388,6 +406,9 @@ enum {
};
enum {
+ FD_SR0_INT = 0x01, /* flag for drv->status0 */
+ FD_SR0_UNITSEL = 0x03,
+ FD_SR0_NOTRDY = 0x08,
FD_SR0_EQPMT = 0x10,
FD_SR0_SEEK = 0x20,
FD_SR0_ABNTERM = 0x40,
@@ -405,6 +426,12 @@ enum {
};
enum {
+ FD_SR3_TS = 0x08,
+ FD_SR3_RDY = 0x20,
+ FD_SR3_FAULT = 0x80,
+};
+
+enum {
FD_SRA_DIR = 0x01,
FD_SRA_nWP = 0x02,
FD_SRA_nINDX = 0x04,
@@ -425,11 +452,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,
@@ -439,11 +462,7 @@ enum {
};
enum {
-#if MAX_FD == 4
FD_TDR_BOOTSEL = 0x0c,
-#else
- FD_TDR_BOOTSEL = 0x04,
-#endif
};
enum {
@@ -467,10 +486,56 @@ enum {
FD_DIR_DSKCHG = 0x80,
};
+enum {
+ PC98_SW_TYP0 = 0x04,
+ PC98_SW_TYP1 = 0x08,
+ PC98_SW_RDY = 0x10,
+ PC98_SW_DMACH = 0x20,
+ PC98_SW_FINT0 = 0x40,
+ PC98_SW_FINT1 = 0x80,
+};
+
+enum {
+ PC98_DOR_MTON = 0x08,
+ PC98_DOR_DMAEN = 0x10,
+ PC98_DOR_AIE = 0x20,
+ PC98_DOR_FRDY = 0x40,
+ PC98_DOR_nRESET = 0x80,
+};
+
+enum {
+ PC98_MODE_PORTEXC = 0x01,
+ PC98_MODE_FDDEXC = 0x02,
+ PC98_MODE_FIX = 0x04,
+ PC98_MODE_DSW = 0x08,
+};
+enum {
+ PC98_MODE_EMTON = 0x04,
+};
+
+enum {
+ PC98_MODE144_MODE = 0x01,
+ PC98_MODE144_EMODE = 0x10,
+ PC98_MODE144_DRVSEL = 0x60,
+};
+
+#define SET_DRV_STATUS0(drv, status) ((drv)->status0 = (status) | FD_SR0_INT)
+
#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;
@@ -493,6 +558,8 @@ struct fdctrl_t {
/* Command FIFO */
uint8_t *fifo;
int32_t fifo_size;
+ uint8_t *fifo_vmstate;
+ int32_t fifo_size_vmstate;
uint32_t data_pos;
uint32_t data_len;
uint8_t data_state;
@@ -509,10 +576,16 @@ struct fdctrl_t {
/* Power down config (also with status regB access mode */
uint8_t pwrd;
/* Sun4m quirks? */
- int sun4m;
+ uint8_t sun4m;
+ /* NEC PC-9821 quirks? */
+ uint8_t pc98;
+ uint8_t frdy;
+ uint8_t if_mode;
+ uint8_t if_mode144;
+ QEMUTimer *media_timer;
/* Floppy drives */
uint8_t num_floppies;
- fdrive_t drives[MAX_FD];
+ fdrive_t drives[MAX_LOGICAL_FD];
int reset_sensei;
};
@@ -648,6 +721,9 @@ static void fdc_pre_save(void *opaque)
{
fdctrl_t *s = opaque;
+ if (s->version != VERSION_NEC_UPD765A) {
+ memcpy(s->fifo_vmstate, s->fifo, s->fifo_size_vmstate);
+ }
s->dor_vmstate = s->dor | GET_CUR_DRV(s);
}
@@ -655,6 +731,9 @@ static int fdc_post_load(void *opaque, int version_id)
{
fdctrl_t *s = opaque;
+ if (s->version != VERSION_NEC_UPD765A) {
+ memcpy(s->fifo, s->fifo_vmstate, s->fifo_size_vmstate);
+ }
SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
return 0;
@@ -679,7 +758,8 @@ static const VMStateDescription vmstate_fdc = {
VMSTATE_UINT8(status1, fdctrl_t),
VMSTATE_UINT8(status2, fdctrl_t),
/* Command FIFO */
- VMSTATE_VARRAY_INT32(fifo, fdctrl_t, fifo_size, 0, vmstate_info_uint8, uint8),
+ VMSTATE_VARRAY_INT32(fifo_vmstate, fdctrl_t, fifo_size_vmstate, 0,
+ vmstate_info_uint8, uint8),
VMSTATE_UINT32(data_pos, fdctrl_t),
VMSTATE_UINT32(data_len, fdctrl_t),
VMSTATE_UINT8(data_state, fdctrl_t),
@@ -693,8 +773,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()
}
};
@@ -781,7 +861,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) {
@@ -803,7 +883,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))
@@ -819,17 +898,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;
}
}
@@ -974,17 +1050,23 @@ static int fdctrl_media_changed(fdrive_t *drv)
return ret;
}
+static int fdctrl_media_inserted(fdrive_t *drv)
+{
+ if (drv->dinfo && drv->bs && bdrv_is_inserted(drv->bs)) {
+ return 1;
+ }
+ return 0;
+}
+
/* Digital input register : 0x07 (read-only) */
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)->dinfo && fdctrl_media_changed(drv0(fdctrl)))
+ || (drv1(fdctrl)->dinfo && fdctrl_media_changed(drv1(fdctrl)))
+ || (drv2(fdctrl)->dinfo && fdctrl_media_changed(drv2(fdctrl)))
+ || (drv3(fdctrl)->dinfo && fdctrl_media_changed(drv3(fdctrl)))
)
retval |= FD_DIR_DSKCHG;
if (retval != 0)
@@ -1059,6 +1141,7 @@ static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
uint8_t status1, uint8_t status2)
{
fdrive_t *cur_drv;
+ int i, bps;
cur_drv = get_cur_drv(fdctrl);
FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
@@ -1070,7 +1153,12 @@ static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
fdctrl->fifo[3] = cur_drv->track;
fdctrl->fifo[4] = cur_drv->head;
fdctrl->fifo[5] = cur_drv->sect;
- fdctrl->fifo[6] = FD_SECTOR_SC;
+ for (i = 0, bps = 128; i < 7; i++, bps <<= 1) {
+ if (cur_drv->bps == bps) {
+ fdctrl->fifo[6] = i;
+ break;
+ }
+ }
fdctrl->data_dir = FD_DIR_READ;
if (!(fdctrl->msr & FD_MSR_NONDMA)) {
DMA_release_DREQ(fdctrl->dma_chann);
@@ -1139,11 +1227,17 @@ static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
if (fdctrl->fifo[5] == 00) {
fdctrl->data_len = fdctrl->fifo[8];
} else {
- int tmp;
+ int tmp = 1;
fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
- tmp = (fdctrl->fifo[6] - ks + 1);
- if (fdctrl->fifo[0] & 0x80)
- tmp += fdctrl->fifo[6];
+ if (fdctrl->version == VERSION_NEC_UPD765A) {
+ if (fdctrl->fifo[0] & 0x80) {
+ tmp = (fdctrl->fifo[6] - ks + 1);
+ }
+ } else {
+ tmp = (fdctrl->fifo[6] - ks + 1);
+ if (fdctrl->fifo[0] & 0x80)
+ tmp += fdctrl->fifo[6];
+ }
fdctrl->data_len *= tmp;
}
fdctrl->eot = fdctrl->fifo[6];
@@ -1177,6 +1271,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 */
+ SET_DRV_STATUS0(cur_drv, 0);
fdctrl_raise_irq(fdctrl, 0x00);
return;
@@ -1213,7 +1308,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
status2 = FD_SR2_SNS;
if (dma_len > fdctrl->data_len)
dma_len = fdctrl->data_len;
- if (cur_drv->bs == NULL) {
+ if (!fdctrl_media_inserted(cur_drv)) {
if (fdctrl->data_dir == FD_DIR_WRITE)
fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
else
@@ -1221,25 +1316,25 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
len = 0;
goto transfer_error;
}
- rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ rel_pos = fdctrl->data_pos % cur_drv->bps;
for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
len = dma_len - fdctrl->data_pos;
- if (len + rel_pos > FD_SECTOR_LEN)
- len = FD_SECTOR_LEN - rel_pos;
+ if (len + rel_pos > cur_drv->bps)
+ len = cur_drv->bps - rel_pos;
FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
"(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
- fd_sector(cur_drv) * FD_SECTOR_LEN);
+ fd_sector(cur_drv) * cur_drv->bps);
if (fdctrl->data_dir != FD_DIR_WRITE ||
- len < FD_SECTOR_LEN || rel_pos != 0) {
+ len < cur_drv->bps || rel_pos != 0) {
/* READ & SCAN commands and realign to a sector for WRITE */
- if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
- fdctrl->fifo, 1) < 0) {
+ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv) * cur_drv->sect_mul,
+ fdctrl->fifo, cur_drv->sect_mul) < 0) {
FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
fd_sector(cur_drv));
/* Sure, image size is too small... */
- memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN_2);
}
}
switch (fdctrl->data_dir) {
@@ -1252,8 +1347,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
/* WRITE commands */
DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
fdctrl->data_pos, len);
- if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
- fdctrl->fifo, 1) < 0) {
+ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv) * cur_drv->sect_mul,
+ fdctrl->fifo, cur_drv->sect_mul) < 0) {
FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
goto transfer_error;
@@ -1262,7 +1357,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
default:
/* SCAN commands */
{
- uint8_t tmpbuf[FD_SECTOR_LEN];
+ uint8_t tmpbuf[FD_SECTOR_LEN_2];
int ret;
DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
@@ -1279,7 +1374,7 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
break;
}
fdctrl->data_pos += len;
- rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ rel_pos = fdctrl->data_pos % cur_drv->bps;
if (rel_pos == 0) {
/* Seek to next sector */
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
@@ -1318,7 +1413,7 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
}
pos = fdctrl->data_pos;
if (fdctrl->msr & FD_MSR_NONDMA) {
- pos %= FD_SECTOR_LEN;
+ pos %= cur_drv->bps;
if (pos == 0) {
if (fdctrl->data_pos != 0)
if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
@@ -1326,11 +1421,12 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
fd_sector(cur_drv));
return 0;
}
- if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv) * cur_drv->sect_mul,
+ fdctrl->fifo, cur_drv->sect_mul) < 0) {
FLOPPY_DPRINTF("error getting sector %d\n",
fd_sector(cur_drv));
/* Sure, image size is too small... */
- memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN_2);
}
}
}
@@ -1393,9 +1489,10 @@ static void fdctrl_format_sector (fdctrl_t *fdctrl)
default:
break;
}
- memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN_2);
if (cur_drv->bs == NULL ||
- bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ bdrv_write(cur_drv->bs, fd_sector(cur_drv) * cur_drv->sect_mul,
+ fdctrl->fifo, cur_drv->sect_mul) < 0) {
FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
} else {
@@ -1426,15 +1523,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)->dinfo ? drv0(fdctrl)->track : 0;
+ fdctrl->fifo[1] = drv1(fdctrl)->dinfo ? drv1(fdctrl)->track : 0;
+ fdctrl->fifo[2] = drv2(fdctrl)->dinfo ? drv2(fdctrl)->track : 0;
+ fdctrl->fifo[3] = drv3(fdctrl)->dinfo ? drv3(fdctrl)->track : 0;
/* timers */
fdctrl->fifo[4] = fdctrl->timer0;
fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
@@ -1464,12 +1556,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)->dinfo) {
+ drv0(fdctrl)->track = fdctrl->fifo[3];
+ }
+ if (drv1(fdctrl)->dinfo) {
+ drv1(fdctrl)->track = fdctrl->fifo[4];
+ }
+ if (drv2(fdctrl)->dinfo) {
+ drv2(fdctrl)->track = fdctrl->fifo[5];
+ }
+ if (drv3(fdctrl)->dinfo) {
+ drv3(fdctrl)->track = fdctrl->fifo[6];
+ }
/* timers */
fdctrl->timer0 = fdctrl->fifo[7];
fdctrl->timer1 = fdctrl->fifo[8];
@@ -1489,15 +1587,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)->dinfo ? drv0(fdctrl)->track : 0;
+ fdctrl->fifo[3] = drv1(fdctrl)->dinfo ? drv1(fdctrl)->track : 0;
+ fdctrl->fifo[4] = drv2(fdctrl)->dinfo ? drv2(fdctrl)->track : 0;
+ fdctrl->fifo[5] = drv3(fdctrl)->dinfo ? drv3(fdctrl)->track : 0;
/* timers */
fdctrl->fifo[6] = fdctrl->timer0;
fdctrl->fifo[7] = fdctrl->timer1;
@@ -1536,6 +1629,7 @@ static void fdctrl_handle_format_track (fdctrl_t *fdctrl, int direction)
fdctrl->data_state &= ~FD_STATE_SEEK;
cur_drv->bps =
fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
+ cur_drv->sect_mul = cur_drv->bps / FD_SECTOR_LEN;
#if 0
cur_drv->last_sect =
cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
@@ -1569,13 +1663,19 @@ static void fdctrl_handle_sense_drive_status (fdctrl_t *fdctrl, int direction)
SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
cur_drv = get_cur_drv(fdctrl);
- cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
/* 1 Byte status back */
- fdctrl->fifo[0] = (cur_drv->ro << 6) |
- (cur_drv->track == 0 ? 0x10 : 0x00) |
- (cur_drv->head << 2) |
- GET_CUR_DRV(fdctrl) |
- 0x28;
+ if (!cur_drv->dinfo) {
+ fdctrl->fifo[0] = GET_CUR_DRV(fdctrl) | FD_SR3_FAULT;
+ } if (!fdctrl_media_inserted(cur_drv)) {
+ fdctrl->fifo[0] = GET_CUR_DRV(fdctrl);
+ } else {
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+ fdctrl->fifo[0] = (cur_drv->ro << 6) |
+ (cur_drv->track == 0 ? 0x10 : 0x00) |
+ (cur_drv->head << 2) |
+ GET_CUR_DRV(fdctrl) |
+ FD_SR3_TS | FD_SR3_RDY;
+ }
fdctrl_set_fifo(fdctrl, 1, 0);
}
@@ -1586,6 +1686,12 @@ 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->dinfo &&
+ ((fdctrl->pc98 && fdctrl->frdy) || fdctrl_media_inserted(cur_drv))) {
+ SET_DRV_STATUS0(cur_drv, FD_SR0_SEEK);
+ } else {
+ SET_DRV_STATUS0(cur_drv, FD_SR0_ABNTERM | FD_SR0_SEEK | FD_SR0_NOTRDY);
+ }
fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */
fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
@@ -1595,21 +1701,57 @@ 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->version == VERSION_NEC_UPD765A) {
+ /* 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 num = -1, i;
+ if (cur_drv->status0 & FD_SR0_INT) {
+ num = GET_CUR_DRV(fdctrl);
+ } else {
+ for (i = 0; i < MAX_LOGICAL_FD; i++) {
+ if (fdctrl->drives[i].status0 & FD_SR0_INT) {
+ num = i;
+ break;
+ }
+ }
+ }
+ if (num != -1) {
+ fdrive_t *drv = &fdctrl->drives[num];
+ fdctrl->fifo[0] = (drv->status0 & ~FD_SR0_UNITSEL) | num;
+ fdctrl->fifo[1] = drv->track;
+ fdctrl_set_fifo(fdctrl, 2, 0);
+ drv->status0 = 0;
+ /* reset irq ? */
+ for (i = 0; i < MAX_LOGICAL_FD; i++) {
+ if (fdctrl->drives[i].status0 & FD_SR0_INT) {
+ break;
+ }
+ }
+ if (i == MAX_LOGICAL_FD) {
+ fdctrl_reset_irq(fdctrl);
+ }
+ } 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;
}
@@ -1621,10 +1763,12 @@ 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) {
+ SET_DRV_STATUS0(cur_drv, FD_SR0_ABNTERM | FD_SR0_SEEK);
fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
} else {
cur_drv->track = fdctrl->fifo[2];
/* Raise Interrupt */
+ SET_DRV_STATUS0(cur_drv, FD_SR0_SEEK);
fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}
}
@@ -1690,8 +1834,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;
+ SET_DRV_STATUS0(cur_drv, FD_SR0_ABNTERM | FD_SR0_SEEK);
} else {
cur_drv->track += fdctrl->fifo[2];
+ SET_DRV_STATUS0(cur_drv, FD_SR0_SEEK);
}
fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */
@@ -1706,8 +1852,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;
+ SET_DRV_STATUS0(cur_drv, FD_SR0_ABNTERM | FD_SR0_SEEK);
} else {
cur_drv->track -= fdctrl->fifo[2];
+ SET_DRV_STATUS0(cur_drv, FD_SR0_SEEK);
}
fdctrl_reset_fifo(fdctrl);
/* Raise Interrupt */
@@ -1715,6 +1863,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;
@@ -1722,38 +1871,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];
@@ -1776,13 +1925,14 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
/* Is it write command time ? */
if (fdctrl->msr & FD_MSR_NONDMA) {
/* FIFO data write */
+ cur_drv = get_cur_drv(fdctrl);
pos = fdctrl->data_pos++;
- pos %= FD_SECTOR_LEN;
+ pos %= cur_drv->bps;
fdctrl->fifo[pos] = value;
- if (pos == FD_SECTOR_LEN - 1 ||
+ if (pos == cur_drv->bps - 1 ||
fdctrl->data_pos == fdctrl->data_len) {
- cur_drv = get_cur_drv(fdctrl);
- if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv) * cur_drv->sect_mul,
+ fdctrl->fifo, cur_drv->sect_mul) < 0) {
FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
return;
}
@@ -1838,12 +1988,183 @@ static void fdctrl_result_timer(void *opaque)
fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}
+/* NEC PC-9821 */
+
+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-9821 */
+ VMSTATE_UINT8(status0, 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 = 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_INT32(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-9821 */
+ VMSTATE_UINT8(frdy, fdctrl_t),
+ VMSTATE_UINT8(if_mode, fdctrl_t),
+ VMSTATE_UINT8(if_mode144, fdctrl_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static uint32_t pc98_fdctrl_read_port (void *opaque, uint32_t reg)
+{
+ fdctrl_t *fdctrl = opaque;
+ uint32_t value = 0xff;
+ int drvsel;
+
+ 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_SW_TYP0 | PC98_SW_FINT0;
+ break;
+ case 0xbe:
+ value = 0xf0 | PC98_MODE_DSW | PC98_MODE_FIX | PC98_MODE_PORTEXC;
+ value |= (fdctrl->if_mode & PC98_MODE_FDDEXC);
+ break;
+ case 0x4be:
+ value = 0xfe;
+ drvsel = (fdctrl->if_mode144 & PC98_MODE144_DRVSEL) >> 5;
+ if (fdctrl->if_mode144 & (1 << drvsel)) {
+ value |= 1;
+ }
+ 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_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;
+ }
+ fdctrl->frdy = ((value & PC98_DOR_FRDY) != 0);
+ if (fdctrl->if_mode & PC98_MODE_EMTON) {
+ if (value & PC98_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->if_mode = value;
+ break;
+ case 0x4be:
+ if (value & PC98_MODE144_EMODE) {
+ uint8_t bit = 1 << ((value & PC98_MODE144_DRVSEL) >> 5);
+ if (value & PC98_MODE144_MODE) {
+ fdctrl->if_mode144 |= bit;
+ } else {
+ fdctrl->if_mode144 &= ~bit;
+ }
+ }
+ fdctrl->if_mode144 &= ~PC98_MODE144_DRVSEL;
+ fdctrl->if_mode144 |= (value & PC98_MODE144_DRVSEL);
+ break;
+ }
+}
+
+static void pc98_fdctrl_media_timer(void *opaque)
+{
+ fdctrl_t *fdctrl = opaque;
+
+ if (!(fdctrl->sra & FD_SRA_INTPEND)) {
+ int i, irq = 0;
+ for (i = 0; i < MAX_FD; i++) {
+ fdrive_t *drv = &fdctrl->drives[i];
+ if (drv->dinfo && fdctrl_media_changed(drv)) {
+ SET_DRV_STATUS0(drv, FD_SR0_RDYCHG);
+ irq = 1;
+ }
+ }
+ if (irq) {
+ fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
+ fdctrl_reset_irq(fdctrl);
+ }
+ }
+
+ /* set next timer */
+ qemu_mod_timer(fdctrl->media_timer,
+ qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10);
+}
+
/* Init functions */
static void fdctrl_connect_drives(fdctrl_t *fdctrl)
{
unsigned int i;
- for (i = 0; i < MAX_FD; i++) {
+ for (i = 0; i < MAX_LOGICAL_FD; i++) {
fd_init(&fdctrl->drives[i]);
fd_revalidate(&fdctrl->drives[i]);
}
@@ -1901,7 +2222,37 @@ fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
return fdctrl;
}
-static int fdctrl_init_common(fdctrl_t *fdctrl)
+fdctrl_t *pc98_fdctrl_init (DriveInfo **fds)
+{
+ ISADevice *dev;
+ fdctrl_t *fdctrl;
+ int i;
+
+ dev = isa_create("pc98-fdc");
+ qdev_prop_set_drive(&dev->qdev, "driveA", fds[0]);
+ qdev_prop_set_drive(&dev->qdev, "driveB", fds[1]);
+ if (qdev_init(&dev->qdev) < 0)
+ return NULL;
+ fdctrl = &(DO_UPCAST(fdctrl_isabus_t, busdev, dev)->state);
+
+ fdctrl->if_mode = PC98_MODE_FDDEXC | PC98_MODE_PORTEXC;
+ fdctrl->dor |= FD_DOR_DMAEN;
+
+ for (i = 0; i < MAX_FD; i++) {
+ fdrive_t *drv = &fdctrl->drives[i];
+ if (drv->dinfo) {
+ fdctrl_media_changed(drv);
+ }
+ }
+ fdctrl->media_timer = qemu_new_timer(vm_clock,
+ pc98_fdctrl_media_timer, fdctrl);
+ qemu_mod_timer(fdctrl->media_timer,
+ qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10);
+
+ return fdctrl;
+}
+
+static int fdctrl_init_common(fdctrl_t *fdctrl, uint8_t version)
{
int i, j;
static int command_tables_inited = 0;
@@ -1910,21 +2261,25 @@ 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 <= version) {
+ for (j = 0; j < sizeof(command_to_handler); j++) {
+ if ((j & handlers[i].mask) == handlers[i].value) {
+ command_to_handler[j] = i;
+ }
}
}
}
}
FLOPPY_DPRINTF("init controller\n");
- fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
- fdctrl->fifo_size = 512;
+ fdctrl->fifo = qemu_memalign(1024, FD_SECTOR_LEN_2);
+ fdctrl->fifo_size = 1024;
+ fdctrl->fifo_vmstate = qemu_memalign(512, FD_SECTOR_LEN);
+ fdctrl->fifo_size_vmstate = 512;
fdctrl->result_timer = qemu_new_timer(vm_clock,
fdctrl_result_timer, fdctrl);
- fdctrl->version = 0x90; /* Intel 82078 controller */
+ fdctrl->version = version;
fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
fdctrl->num_floppies = MAX_FD;
@@ -1955,7 +2310,7 @@ static int isabus_fdc_init1(ISADevice *dev)
isa_init_irq(&isa->busdev, &fdctrl->irq, isairq);
fdctrl->dma_chann = dma_chann;
- ret = fdctrl_init_common(fdctrl);
+ ret = fdctrl_init_common(fdctrl, VERSION_INTEL_82078);
fdctrl_external_reset_isa(&isa->busdev.qdev);
return ret;
@@ -1974,7 +2329,7 @@ static int sysbus_fdc_init1(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
fdctrl->dma_chann = -1;
- ret = fdctrl_init_common(fdctrl);
+ ret = fdctrl_init_common(fdctrl, VERSION_INTEL_82078);
fdctrl_external_reset_sysbus(&sys->busdev.qdev);
return ret;
@@ -1992,7 +2347,32 @@ static int sun4m_fdc_init1(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
fdctrl->sun4m = 1;
- return fdctrl_init_common(fdctrl);
+ return fdctrl_init_common(fdctrl, VERSION_INTEL_82078);
+}
+
+static int pc98_fdc_init1(ISADevice *dev)
+{
+ static const uint32_t port[8] = {
+ 0x90, 0x92, 0x94, 0xc8, 0xca, 0xcc, 0xbe, 0x4be
+ };
+ fdctrl_isabus_t *isa = DO_UPCAST(fdctrl_isabus_t, busdev, dev);
+ fdctrl_t *fdctrl = &isa->state;
+ int isairq = 11;
+ int dma_chann = 2;
+ int i, ret;
+
+ 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->irq, isairq);
+ fdctrl->dma_chann = dma_chann;
+
+ fdctrl->pc98 = 1;
+ ret = fdctrl_init_common(fdctrl, VERSION_NEC_UPD765A);
+ fdctrl_external_reset_isa(&isa->busdev.qdev);
+
+ return ret;
}
static ISADeviceInfo isa_fdc_info = {
@@ -2033,11 +2413,25 @@ static SysBusDeviceInfo sun4m_fdc_info = {
},
};
+static ISADeviceInfo pc98_fdc_info = {
+ .init = pc98_fdc_init1,
+ .qdev.name = "pc98-fdc",
+ .qdev.size = sizeof(fdctrl_isabus_t),
+ .qdev.vmsd = &vmstate_pc98_fdc,
+ .qdev.reset = fdctrl_external_reset_isa,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("driveA", fdctrl_isabus_t, state.drives[0].dinfo),
+ DEFINE_PROP_DRIVE("driveB", fdctrl_isabus_t, state.drives[1].dinfo),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
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)
@@ -10,4 +10,5 @@ fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
DriveInfo **fds);
fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
DriveInfo **fds, qemu_irq *fdc_tc);
+fdctrl_t *pc98_fdctrl_init (DriveInfo **fds);
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num);