From patchwork Wed Oct 28 16:51:36 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?5q2m55SwID0/SVNPLTIwMjItSlA/Qj9JQnNrUWoxVFRHa2JLRUk9Pz0=?= X-Patchwork-Id: 37133 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C64C6100DA2 for ; Thu, 29 Oct 2009 08:53:26 +1100 (EST) Received: from localhost ([127.0.0.1]:57965 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N3Cbx-0007N9-Rr for incoming@patchwork.ozlabs.org; Wed, 28 Oct 2009 13:47:05 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N3Bn7-0007b0-6X for qemu-devel@nongnu.org; Wed, 28 Oct 2009 12:54:33 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N3Bmy-0007Q1-AR for qemu-devel@nongnu.org; Wed, 28 Oct 2009 12:54:28 -0400 Received: from [199.232.76.173] (port=45985 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N3Bmy-0007Pa-0W for qemu-devel@nongnu.org; Wed, 28 Oct 2009 12:54:24 -0400 Received: from smtp-vip.mem.interq.net ([210.157.1.50]:30844 helo=smtp01.mem.internal-gmo) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N3Bmw-0002lE-HK for qemu-devel@nongnu.org; Wed, 28 Oct 2009 12:54:23 -0400 Received: (from root@localhost) by smtp01.mem.internal-gmo (8.13.8/8.12.6) id n9SGs7xJ018693 for qemu-devel@nongnu.org; Thu, 29 Oct 2009 01:54:07 +0900 (JST) Received: from YOUR-BD18D6DD63.m1.interq.or.jp (ntymns039132.ymns.nt.ftth.ppp.infoweb.ne.jp [121.92.167.132]) by smtp01.mem.internal-gmo with ESMTP id n9SGs5ZT018647 for ; (me101664 for with PLAIN) Thu, 29 Oct 2009 01:54:06 +0900 (JST) Message-Id: <200910281651.AA00171@YOUR-BD18D6DD63.m1.interq.or.jp> Date: Thu, 29 Oct 2009 01:51:36 +0900 To: qemu-devel From: "TAKEDA, toshiya" MIME-Version: 1.0 X-Mailer: AL-Mail32 Version 1.13 X-detected-operating-system: by monty-python.gnu.org: Solaris 10 (beta) Subject: [Qemu-devel] [PATCH v3 7/25] fdc: add NEC PC-9821 family interface and support 1.23MB diskette X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch includes the modifications for 1.23MB floppy diskette. Its sector size is 1024bytes, so I change the fifo buffer size to 1024, and add fifo_vmstate and fifo_size_vmstate for vmstate compatibility. hw/hw.h modification in this patch is by Mr.Juan Quintela, thanks! diff --git a/qemu/hw/hw.h b/qemu/hw/hw.h index eb5c639..f0de6a2 100644 --- a/qemu/hw/hw.h +++ b/qemu/hw/hw.h @@ -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) { \ diff --git a/qemu/hw/fdc.c b/qemu/hw/fdc.c index dbf93e8..6602144 100644 --- a/qemu/hw/fdc.c +++ b/qemu/hw/fdc.c @@ -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) diff --git a/qemu/hw/fdc.h b/qemu/hw/fdc.h index c64e8b4..56ce0b7 100644 --- a/qemu/hw/fdc.h +++ b/qemu/hw/fdc.h @@ -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);