📄 fdc.c
字号:
/* * QEMU Floppy disk emulator (Intel 82078) * * Copyright (c) 2003 Jocelyn Mayer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *//* * The controller is used in Sun4m systems in a slightly different * way. There are changes in DOR register and DMA is not available. */#include "vl.h"/********************************************************//* debug Floppy devices *///#define DEBUG_FLOPPY#ifdef DEBUG_FLOPPY#define FLOPPY_DPRINTF(fmt, args...) \do { printf("FLOPPY: " fmt , ##args); } while (0)#else#define FLOPPY_DPRINTF(fmt, args...)#endif#define FLOPPY_ERROR(fmt, args...) \do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)/********************************************************//* Floppy drive emulation *//* Will always be a fixed parameter for us */#define FD_SECTOR_LEN 512#define FD_SECTOR_SC 2 /* Sector size code *//* Floppy disk drive emulation */typedef enum fdisk_type_t { FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */ FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */ FDRIVE_DISK_720 = 0x03, /* 720 kB disk */ FDRIVE_DISK_USER = 0x04, /* User defined geometry */ FDRIVE_DISK_NONE = 0x05, /* No disk */} fdisk_type_t;typedef enum fdrive_type_t { FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ FDRIVE_DRV_NONE = 0x03, /* No drive connected */} fdrive_type_t;typedef enum fdrive_flags_t { FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ FDRIVE_REVALIDATE = 0x02, /* Revalidated */} fdrive_flags_t;typedef enum fdisk_flags_t { FDISK_DBL_SIDES = 0x01,} fdisk_flags_t;typedef struct fdrive_t { BlockDriverState *bs; /* Drive status */ fdrive_type_t drive; fdrive_flags_t drflags; uint8_t perpendicular; /* 2.88 MB access mode */ /* Position */ uint8_t head; uint8_t track; uint8_t sect; /* Last operation status */ uint8_t dir; /* Direction */ uint8_t rw; /* Read/write */ /* Media */ fdisk_flags_t flags; uint8_t last_sect; /* Nb sector per track */ uint8_t max_track; /* Nb of tracks */ uint16_t bps; /* Bytes per sector */ uint8_t ro; /* Is read-only */} fdrive_t;static void fd_init (fdrive_t *drv, BlockDriverState *bs){ /* Drive */ drv->bs = bs; drv->drive = FDRIVE_DRV_NONE; drv->drflags = 0; drv->perpendicular = 0; /* Disk */ drv->last_sect = 0; drv->max_track = 0;}static int _fd_sector (uint8_t head, uint8_t track, uint8_t sect, uint8_t last_sect){ return (((track * 2) + head) * last_sect) + sect - 1;}/* Returns current position, in sectors, for given drive */static int fd_sector (fdrive_t *drv){ return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);}static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, int enable_seek){ uint32_t sector; int ret; if (track > drv->max_track || (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", head, track, sect, 1, (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, drv->max_track, drv->last_sect); return 2; } if (sect > drv->last_sect) { FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", head, track, sect, 1, (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, drv->max_track, drv->last_sect); return 3; } sector = _fd_sector(head, track, sect, drv->last_sect); ret = 0; if (sector != fd_sector(drv)) {#if 0 if (!enable_seek) { FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n", head, track, sect, 1, drv->max_track, drv->last_sect); return 4; }#endif drv->head = head; if (drv->track != track) ret = 1; drv->track = track; drv->sect = sect; } return ret;}/* Set drive back to track 0 */static void fd_recalibrate (fdrive_t *drv){ FLOPPY_DPRINTF("recalibrate\n"); drv->head = 0; drv->track = 0; drv->sect = 1; drv->dir = 1; drv->rw = 0;}/* Recognize floppy formats */typedef struct fd_format_t { fdrive_type_t drive; fdisk_type_t disk; uint8_t last_sect; uint8_t max_track; uint8_t max_head; const unsigned char *str;} fd_format_t;static 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", }, /* 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", }, /* 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", }, /* 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", }, /* 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", }, /* 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", }, /* 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", }, /* 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", }, /* end */ { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },};/* Revalidate a disk drive after a disk change */static void fd_revalidate (fdrive_t *drv){ fd_format_t *parse; int64_t nb_sectors, size; int i, first_match, match; int nb_heads, max_track, last_sect, ro; FLOPPY_DPRINTF("revalidate\n"); drv->drflags &= ~FDRIVE_REVALIDATE; if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ro = bdrv_is_read_only(drv->bs); bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); if (nb_heads != 0 && max_track != 0 && last_sect != 0) { FLOPPY_DPRINTF("User defined disk (%d %d %d)", nb_heads - 1, max_track, last_sect); } else { bdrv_get_geometry(drv->bs, &nb_sectors); match = -1; first_match = -1; for (i = 0;; i++) { parse = &fd_formats[i]; if (parse->drive == FDRIVE_DRV_NONE) break; if (drv->drive == parse->drive || drv->drive == FDRIVE_DRV_NONE) { size = (parse->max_head + 1) * parse->max_track * parse->last_sect; if (nb_sectors == size) { match = i; break; } if (first_match == -1) first_match = i; } } if (match == -1) { if (first_match == -1) match = 1; else match = first_match; parse = &fd_formats[match]; } nb_heads = parse->max_head + 1; max_track = parse->max_track; last_sect = parse->last_sect; 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"); } if (nb_heads == 1) { drv->flags &= ~FDISK_DBL_SIDES; } else { drv->flags |= FDISK_DBL_SIDES; } drv->max_track = max_track; drv->last_sect = last_sect; drv->ro = ro; } else { FLOPPY_DPRINTF("No disk in drive\n"); drv->last_sect = 0; drv->max_track = 0; drv->flags &= ~FDISK_DBL_SIDES; } drv->drflags |= FDRIVE_REVALIDATE;}/* Motor control */static void fd_start (fdrive_t *drv){ drv->drflags |= FDRIVE_MOTOR_ON;}static void fd_stop (fdrive_t *drv){ drv->drflags &= ~FDRIVE_MOTOR_ON;}/* Re-initialise a drives (motor off, repositioned) */static void fd_reset (fdrive_t *drv){ fd_stop(drv); fd_recalibrate(drv);}/********************************************************//* Intel 82078 floppy disk controller emulation */static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);static void fdctrl_reset_fifo (fdctrl_t *fdctrl);static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len);static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);static void fdctrl_result_timer(void *opaque);static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl);static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl);static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value);static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl);static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value);static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl);static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value);static uint32_t fdctrl_read_data (fdctrl_t *fdctrl);static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value);static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl);enum { FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */ FD_CTRL_RESET = 0x02, FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */ FD_CTRL_BUSY = 0x08, /* dma transfer in progress */ FD_CTRL_INTR = 0x10,};enum { FD_DIR_WRITE = 0, FD_DIR_READ = 1, FD_DIR_SCANE = 2, FD_DIR_SCANL = 3, FD_DIR_SCANH = 4,};enum { FD_STATE_CMD = 0x00, FD_STATE_STATUS = 0x01, FD_STATE_DATA = 0x02, FD_STATE_STATE = 0x03, FD_STATE_MULTI = 0x10, FD_STATE_SEEK = 0x20, FD_STATE_FORMAT = 0x40,};#define FD_STATE(state) ((state) & FD_STATE_STATE)#define FD_SET_STATE(state, new_state) \do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)#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)struct fdctrl_t { fdctrl_t *fdctrl; /* Controller's identification */ uint8_t version; /* HW */ int irq_lvl; int dma_chann; uint32_t io_base; /* Controller state */ QEMUTimer *result_timer; uint8_t state; uint8_t dma_en; uint8_t cur_drv; uint8_t bootsel; /* Command FIFO */ uint8_t fifo[FD_SECTOR_LEN]; uint32_t data_pos; uint32_t data_len; uint8_t data_state; uint8_t data_dir; uint8_t int_status; uint8_t eot; /* last wanted sector */ /* States kept only to be returned back */ /* Timers state */ uint8_t timer0; uint8_t timer1; /* precompensation */ uint8_t precomp_trk; uint8_t config; uint8_t lock; /* Power down config (also with status regB access mode */ uint8_t pwrd; /* Floppy drives */ fdrive_t drives[2];};static uint32_t fdctrl_read (void *opaque, uint32_t reg){ fdctrl_t *fdctrl = opaque; uint32_t retval; switch (reg & 0x07) {#ifdef TARGET_SPARC case 0x00: // Identify to Linux as S82078B retval = fdctrl_read_statusB(fdctrl); break;#endif case 0x01: retval = fdctrl_read_statusB(fdctrl); break; case 0x02: retval = fdctrl_read_dor(fdctrl); break; case 0x03: retval = fdctrl_read_tape(fdctrl); break; case 0x04: retval = fdctrl_read_main_status(fdctrl); break; case 0x05: retval = fdctrl_read_data(fdctrl); break; case 0x07: retval = fdctrl_read_dir(fdctrl); break; default: retval = (uint32_t)(-1); break; } FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -