|
|
1.1 ! root 1: /* ! 2: * QEMU Floppy disk emulator (Intel 82078) ! 3: * ! 4: * Copyright (c) 2003 Jocelyn Mayer ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: /* ! 25: * The controller is used in Sun4m systems in a slightly different ! 26: * way. There are changes in DOR register and DMA is not available. ! 27: */ ! 28: #include "vl.h" ! 29: ! 30: /********************************************************/ ! 31: /* debug Floppy devices */ ! 32: //#define DEBUG_FLOPPY ! 33: ! 34: #ifdef DEBUG_FLOPPY ! 35: #define FLOPPY_DPRINTF(fmt, args...) \ ! 36: do { printf("FLOPPY: " fmt , ##args); } while (0) ! 37: #else ! 38: #define FLOPPY_DPRINTF(fmt, args...) ! 39: #endif ! 40: ! 41: #define FLOPPY_ERROR(fmt, args...) \ ! 42: do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0) ! 43: ! 44: /********************************************************/ ! 45: /* Floppy drive emulation */ ! 46: ! 47: /* Will always be a fixed parameter for us */ ! 48: #define FD_SECTOR_LEN 512 ! 49: #define FD_SECTOR_SC 2 /* Sector size code */ ! 50: ! 51: /* Floppy disk drive emulation */ ! 52: typedef enum fdisk_type_t { ! 53: FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */ ! 54: FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */ ! 55: FDRIVE_DISK_720 = 0x03, /* 720 kB disk */ ! 56: FDRIVE_DISK_USER = 0x04, /* User defined geometry */ ! 57: FDRIVE_DISK_NONE = 0x05, /* No disk */ ! 58: } fdisk_type_t; ! 59: ! 60: typedef enum fdrive_type_t { ! 61: FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ ! 62: FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ ! 63: FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ ! 64: FDRIVE_DRV_NONE = 0x03, /* No drive connected */ ! 65: } fdrive_type_t; ! 66: ! 67: typedef enum fdrive_flags_t { ! 68: FDRIVE_MOTOR_ON = 0x01, /* motor on/off */ ! 69: FDRIVE_REVALIDATE = 0x02, /* Revalidated */ ! 70: } fdrive_flags_t; ! 71: ! 72: typedef enum fdisk_flags_t { ! 73: FDISK_DBL_SIDES = 0x01, ! 74: } fdisk_flags_t; ! 75: ! 76: typedef struct fdrive_t { ! 77: BlockDriverState *bs; ! 78: /* Drive status */ ! 79: fdrive_type_t drive; ! 80: fdrive_flags_t drflags; ! 81: uint8_t perpendicular; /* 2.88 MB access mode */ ! 82: /* Position */ ! 83: uint8_t head; ! 84: uint8_t track; ! 85: uint8_t sect; ! 86: /* Last operation status */ ! 87: uint8_t dir; /* Direction */ ! 88: uint8_t rw; /* Read/write */ ! 89: /* Media */ ! 90: fdisk_flags_t flags; ! 91: uint8_t last_sect; /* Nb sector per track */ ! 92: uint8_t max_track; /* Nb of tracks */ ! 93: uint16_t bps; /* Bytes per sector */ ! 94: uint8_t ro; /* Is read-only */ ! 95: } fdrive_t; ! 96: ! 97: static void fd_init (fdrive_t *drv, BlockDriverState *bs) ! 98: { ! 99: /* Drive */ ! 100: drv->bs = bs; ! 101: drv->drive = FDRIVE_DRV_NONE; ! 102: drv->drflags = 0; ! 103: drv->perpendicular = 0; ! 104: /* Disk */ ! 105: drv->last_sect = 0; ! 106: drv->max_track = 0; ! 107: } ! 108: ! 109: static int _fd_sector (uint8_t head, uint8_t track, ! 110: uint8_t sect, uint8_t last_sect) ! 111: { ! 112: return (((track * 2) + head) * last_sect) + sect - 1; ! 113: } ! 114: ! 115: /* Returns current position, in sectors, for given drive */ ! 116: static int fd_sector (fdrive_t *drv) ! 117: { ! 118: return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect); ! 119: } ! 120: ! 121: static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, ! 122: int enable_seek) ! 123: { ! 124: uint32_t sector; ! 125: int ret; ! 126: ! 127: if (track > drv->max_track || ! 128: (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) { ! 129: FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", ! 130: head, track, sect, 1, ! 131: (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, ! 132: drv->max_track, drv->last_sect); ! 133: return 2; ! 134: } ! 135: if (sect > drv->last_sect) { ! 136: FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n", ! 137: head, track, sect, 1, ! 138: (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1, ! 139: drv->max_track, drv->last_sect); ! 140: return 3; ! 141: } ! 142: sector = _fd_sector(head, track, sect, drv->last_sect); ! 143: ret = 0; ! 144: if (sector != fd_sector(drv)) { ! 145: #if 0 ! 146: if (!enable_seek) { ! 147: FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n", ! 148: head, track, sect, 1, drv->max_track, drv->last_sect); ! 149: return 4; ! 150: } ! 151: #endif ! 152: drv->head = head; ! 153: if (drv->track != track) ! 154: ret = 1; ! 155: drv->track = track; ! 156: drv->sect = sect; ! 157: } ! 158: ! 159: return ret; ! 160: } ! 161: ! 162: /* Set drive back to track 0 */ ! 163: static void fd_recalibrate (fdrive_t *drv) ! 164: { ! 165: FLOPPY_DPRINTF("recalibrate\n"); ! 166: drv->head = 0; ! 167: drv->track = 0; ! 168: drv->sect = 1; ! 169: drv->dir = 1; ! 170: drv->rw = 0; ! 171: } ! 172: ! 173: /* Recognize floppy formats */ ! 174: typedef struct fd_format_t { ! 175: fdrive_type_t drive; ! 176: fdisk_type_t disk; ! 177: uint8_t last_sect; ! 178: uint8_t max_track; ! 179: uint8_t max_head; ! 180: const unsigned char *str; ! 181: } fd_format_t; ! 182: ! 183: static fd_format_t fd_formats[] = { ! 184: /* First entry is default format */ ! 185: /* 1.44 MB 3"1/2 floppy disks */ ! 186: { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", }, ! 187: { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", }, ! 188: { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", }, ! 189: { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", }, ! 190: { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", }, ! 191: { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", }, ! 192: { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", }, ! 193: { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", }, ! 194: /* 2.88 MB 3"1/2 floppy disks */ ! 195: { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", }, ! 196: { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", }, ! 197: { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", }, ! 198: { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", }, ! 199: { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", }, ! 200: /* 720 kB 3"1/2 floppy disks */ ! 201: { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", }, ! 202: { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", }, ! 203: { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", }, ! 204: { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", }, ! 205: { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", }, ! 206: { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", }, ! 207: /* 1.2 MB 5"1/4 floppy disks */ ! 208: { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", }, ! 209: { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", }, ! 210: { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", }, ! 211: { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", }, ! 212: { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", }, ! 213: /* 720 kB 5"1/4 floppy disks */ ! 214: { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", }, ! 215: { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", }, ! 216: /* 360 kB 5"1/4 floppy disks */ ! 217: { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", }, ! 218: { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", }, ! 219: { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", }, ! 220: { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", }, ! 221: /* 320 kB 5"1/4 floppy disks */ ! 222: { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", }, ! 223: { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", }, ! 224: /* 360 kB must match 5"1/4 better than 3"1/2... */ ! 225: { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", }, ! 226: /* end */ ! 227: { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, }, ! 228: }; ! 229: ! 230: /* Revalidate a disk drive after a disk change */ ! 231: static void fd_revalidate (fdrive_t *drv) ! 232: { ! 233: fd_format_t *parse; ! 234: int64_t nb_sectors, size; ! 235: int i, first_match, match; ! 236: int nb_heads, max_track, last_sect, ro; ! 237: ! 238: FLOPPY_DPRINTF("revalidate\n"); ! 239: drv->drflags &= ~FDRIVE_REVALIDATE; ! 240: if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) { ! 241: ro = bdrv_is_read_only(drv->bs); ! 242: bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect); ! 243: if (nb_heads != 0 && max_track != 0 && last_sect != 0) { ! 244: FLOPPY_DPRINTF("User defined disk (%d %d %d)", ! 245: nb_heads - 1, max_track, last_sect); ! 246: } else { ! 247: bdrv_get_geometry(drv->bs, &nb_sectors); ! 248: match = -1; ! 249: first_match = -1; ! 250: for (i = 0;; i++) { ! 251: parse = &fd_formats[i]; ! 252: if (parse->drive == FDRIVE_DRV_NONE) ! 253: break; ! 254: if (drv->drive == parse->drive || ! 255: drv->drive == FDRIVE_DRV_NONE) { ! 256: size = (parse->max_head + 1) * parse->max_track * ! 257: parse->last_sect; ! 258: if (nb_sectors == size) { ! 259: match = i; ! 260: break; ! 261: } ! 262: if (first_match == -1) ! 263: first_match = i; ! 264: } ! 265: } ! 266: if (match == -1) { ! 267: if (first_match == -1) ! 268: match = 1; ! 269: else ! 270: match = first_match; ! 271: parse = &fd_formats[match]; ! 272: } ! 273: nb_heads = parse->max_head + 1; ! 274: max_track = parse->max_track; ! 275: last_sect = parse->last_sect; ! 276: drv->drive = parse->drive; ! 277: FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str, ! 278: nb_heads, max_track, last_sect, ro ? "ro" : "rw"); ! 279: } ! 280: if (nb_heads == 1) { ! 281: drv->flags &= ~FDISK_DBL_SIDES; ! 282: } else { ! 283: drv->flags |= FDISK_DBL_SIDES; ! 284: } ! 285: drv->max_track = max_track; ! 286: drv->last_sect = last_sect; ! 287: drv->ro = ro; ! 288: } else { ! 289: FLOPPY_DPRINTF("No disk in drive\n"); ! 290: drv->last_sect = 0; ! 291: drv->max_track = 0; ! 292: drv->flags &= ~FDISK_DBL_SIDES; ! 293: } ! 294: drv->drflags |= FDRIVE_REVALIDATE; ! 295: } ! 296: ! 297: /* Motor control */ ! 298: static void fd_start (fdrive_t *drv) ! 299: { ! 300: drv->drflags |= FDRIVE_MOTOR_ON; ! 301: } ! 302: ! 303: static void fd_stop (fdrive_t *drv) ! 304: { ! 305: drv->drflags &= ~FDRIVE_MOTOR_ON; ! 306: } ! 307: ! 308: /* Re-initialise a drives (motor off, repositioned) */ ! 309: static void fd_reset (fdrive_t *drv) ! 310: { ! 311: fd_stop(drv); ! 312: fd_recalibrate(drv); ! 313: } ! 314: ! 315: /********************************************************/ ! 316: /* Intel 82078 floppy disk controller emulation */ ! 317: ! 318: static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); ! 319: static void fdctrl_reset_fifo (fdctrl_t *fdctrl); ! 320: static int fdctrl_transfer_handler (void *opaque, int nchan, ! 321: int dma_pos, int dma_len); ! 322: static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status); ! 323: static void fdctrl_result_timer(void *opaque); ! 324: ! 325: static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); ! 326: static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl); ! 327: static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value); ! 328: static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl); ! 329: static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value); ! 330: static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl); ! 331: static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value); ! 332: static uint32_t fdctrl_read_data (fdctrl_t *fdctrl); ! 333: static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value); ! 334: static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl); ! 335: ! 336: enum { ! 337: FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */ ! 338: FD_CTRL_RESET = 0x02, ! 339: FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */ ! 340: FD_CTRL_BUSY = 0x08, /* dma transfer in progress */ ! 341: FD_CTRL_INTR = 0x10, ! 342: }; ! 343: ! 344: enum { ! 345: FD_DIR_WRITE = 0, ! 346: FD_DIR_READ = 1, ! 347: FD_DIR_SCANE = 2, ! 348: FD_DIR_SCANL = 3, ! 349: FD_DIR_SCANH = 4, ! 350: }; ! 351: ! 352: enum { ! 353: FD_STATE_CMD = 0x00, ! 354: FD_STATE_STATUS = 0x01, ! 355: FD_STATE_DATA = 0x02, ! 356: FD_STATE_STATE = 0x03, ! 357: FD_STATE_MULTI = 0x10, ! 358: FD_STATE_SEEK = 0x20, ! 359: FD_STATE_FORMAT = 0x40, ! 360: }; ! 361: ! 362: #define FD_STATE(state) ((state) & FD_STATE_STATE) ! 363: #define FD_SET_STATE(state, new_state) \ ! 364: do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0) ! 365: #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI) ! 366: #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) ! 367: #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) ! 368: ! 369: struct fdctrl_t { ! 370: fdctrl_t *fdctrl; ! 371: /* Controller's identification */ ! 372: uint8_t version; ! 373: /* HW */ ! 374: int irq_lvl; ! 375: int dma_chann; ! 376: uint32_t io_base; ! 377: /* Controller state */ ! 378: QEMUTimer *result_timer; ! 379: uint8_t state; ! 380: uint8_t dma_en; ! 381: uint8_t cur_drv; ! 382: uint8_t bootsel; ! 383: /* Command FIFO */ ! 384: uint8_t fifo[FD_SECTOR_LEN]; ! 385: uint32_t data_pos; ! 386: uint32_t data_len; ! 387: uint8_t data_state; ! 388: uint8_t data_dir; ! 389: uint8_t int_status; ! 390: uint8_t eot; /* last wanted sector */ ! 391: /* States kept only to be returned back */ ! 392: /* Timers state */ ! 393: uint8_t timer0; ! 394: uint8_t timer1; ! 395: /* precompensation */ ! 396: uint8_t precomp_trk; ! 397: uint8_t config; ! 398: uint8_t lock; ! 399: /* Power down config (also with status regB access mode */ ! 400: uint8_t pwrd; ! 401: /* Floppy drives */ ! 402: fdrive_t drives[2]; ! 403: }; ! 404: ! 405: static uint32_t fdctrl_read (void *opaque, uint32_t reg) ! 406: { ! 407: fdctrl_t *fdctrl = opaque; ! 408: uint32_t retval; ! 409: ! 410: switch (reg & 0x07) { ! 411: #ifdef TARGET_SPARC ! 412: case 0x00: ! 413: // Identify to Linux as S82078B ! 414: retval = fdctrl_read_statusB(fdctrl); ! 415: break; ! 416: #endif ! 417: case 0x01: ! 418: retval = fdctrl_read_statusB(fdctrl); ! 419: break; ! 420: case 0x02: ! 421: retval = fdctrl_read_dor(fdctrl); ! 422: break; ! 423: case 0x03: ! 424: retval = fdctrl_read_tape(fdctrl); ! 425: break; ! 426: case 0x04: ! 427: retval = fdctrl_read_main_status(fdctrl); ! 428: break; ! 429: case 0x05: ! 430: retval = fdctrl_read_data(fdctrl); ! 431: break; ! 432: case 0x07: ! 433: retval = fdctrl_read_dir(fdctrl); ! 434: break; ! 435: default: ! 436: retval = (uint32_t)(-1); ! 437: break; ! 438: } ! 439: FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); ! 440: ! 441: return retval; ! 442: } ! 443: ! 444: static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) ! 445: { ! 446: fdctrl_t *fdctrl = opaque; ! 447: ! 448: FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); ! 449: ! 450: switch (reg & 0x07) { ! 451: case 0x02: ! 452: fdctrl_write_dor(fdctrl, value); ! 453: break; ! 454: case 0x03: ! 455: fdctrl_write_tape(fdctrl, value); ! 456: break; ! 457: case 0x04: ! 458: fdctrl_write_rate(fdctrl, value); ! 459: break; ! 460: case 0x05: ! 461: fdctrl_write_data(fdctrl, value); ! 462: break; ! 463: default: ! 464: break; ! 465: } ! 466: } ! 467: ! 468: static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg) ! 469: { ! 470: return fdctrl_read(opaque, reg); ! 471: } ! 472: ! 473: static void fdctrl_write_mem (void *opaque, ! 474: target_phys_addr_t reg, uint32_t value) ! 475: { ! 476: fdctrl_write(opaque, reg, value); ! 477: } ! 478: ! 479: static CPUReadMemoryFunc *fdctrl_mem_read[3] = { ! 480: fdctrl_read_mem, ! 481: fdctrl_read_mem, ! 482: fdctrl_read_mem, ! 483: }; ! 484: ! 485: static CPUWriteMemoryFunc *fdctrl_mem_write[3] = { ! 486: fdctrl_write_mem, ! 487: fdctrl_write_mem, ! 488: fdctrl_write_mem, ! 489: }; ! 490: ! 491: static void fd_change_cb (void *opaque) ! 492: { ! 493: fdrive_t *drv = opaque; ! 494: ! 495: FLOPPY_DPRINTF("disk change\n"); ! 496: fd_revalidate(drv); ! 497: #if 0 ! 498: fd_recalibrate(drv); ! 499: fdctrl_reset_fifo(drv->fdctrl); ! 500: fdctrl_raise_irq(drv->fdctrl, 0x20); ! 501: #endif ! 502: } ! 503: ! 504: fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, ! 505: uint32_t io_base, ! 506: BlockDriverState **fds) ! 507: { ! 508: fdctrl_t *fdctrl; ! 509: int io_mem; ! 510: int i; ! 511: ! 512: FLOPPY_DPRINTF("init controller\n"); ! 513: fdctrl = qemu_mallocz(sizeof(fdctrl_t)); ! 514: if (!fdctrl) ! 515: return NULL; ! 516: fdctrl->result_timer = qemu_new_timer(vm_clock, ! 517: fdctrl_result_timer, fdctrl); ! 518: ! 519: fdctrl->version = 0x90; /* Intel 82078 controller */ ! 520: fdctrl->irq_lvl = irq_lvl; ! 521: fdctrl->dma_chann = dma_chann; ! 522: fdctrl->io_base = io_base; ! 523: fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */ ! 524: if (fdctrl->dma_chann != -1) { ! 525: fdctrl->dma_en = 1; ! 526: DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl); ! 527: } else { ! 528: fdctrl->dma_en = 0; ! 529: } ! 530: for (i = 0; i < 2; i++) { ! 531: fd_init(&fdctrl->drives[i], fds[i]); ! 532: if (fds[i]) { ! 533: bdrv_set_change_cb(fds[i], ! 534: &fd_change_cb, &fdctrl->drives[i]); ! 535: } ! 536: } ! 537: fdctrl_reset(fdctrl, 0); ! 538: fdctrl->state = FD_CTRL_ACTIVE; ! 539: if (mem_mapped) { ! 540: io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl); ! 541: cpu_register_physical_memory(io_base, 0x08, io_mem); ! 542: } else { ! 543: register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl); ! 544: register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl); ! 545: register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl); ! 546: register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl); ! 547: } ! 548: for (i = 0; i < 2; i++) { ! 549: fd_revalidate(&fdctrl->drives[i]); ! 550: } ! 551: ! 552: return fdctrl; ! 553: } ! 554: ! 555: /* XXX: may change if moved to bdrv */ ! 556: int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) ! 557: { ! 558: return fdctrl->drives[drive_num].drive; ! 559: } ! 560: ! 561: /* Change IRQ state */ ! 562: static void fdctrl_reset_irq (fdctrl_t *fdctrl) ! 563: { ! 564: FLOPPY_DPRINTF("Reset interrupt\n"); ! 565: pic_set_irq(fdctrl->irq_lvl, 0); ! 566: fdctrl->state &= ~FD_CTRL_INTR; ! 567: } ! 568: ! 569: static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status) ! 570: { ! 571: #ifdef TARGET_SPARC ! 572: // Sparc mutation ! 573: if (!fdctrl->dma_en) { ! 574: fdctrl->state &= ~FD_CTRL_BUSY; ! 575: fdctrl->int_status = status; ! 576: return; ! 577: } ! 578: #endif ! 579: if (~(fdctrl->state & FD_CTRL_INTR)) { ! 580: pic_set_irq(fdctrl->irq_lvl, 1); ! 581: fdctrl->state |= FD_CTRL_INTR; ! 582: } ! 583: FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status); ! 584: fdctrl->int_status = status; ! 585: } ! 586: ! 587: /* Reset controller */ ! 588: static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) ! 589: { ! 590: int i; ! 591: ! 592: FLOPPY_DPRINTF("reset controller\n"); ! 593: fdctrl_reset_irq(fdctrl); ! 594: /* Initialise controller */ ! 595: fdctrl->cur_drv = 0; ! 596: /* FIFO state */ ! 597: fdctrl->data_pos = 0; ! 598: fdctrl->data_len = 0; ! 599: fdctrl->data_state = FD_STATE_CMD; ! 600: fdctrl->data_dir = FD_DIR_WRITE; ! 601: for (i = 0; i < MAX_FD; i++) ! 602: fd_reset(&fdctrl->drives[i]); ! 603: fdctrl_reset_fifo(fdctrl); ! 604: if (do_irq) ! 605: fdctrl_raise_irq(fdctrl, 0xc0); ! 606: } ! 607: ! 608: static inline fdrive_t *drv0 (fdctrl_t *fdctrl) ! 609: { ! 610: return &fdctrl->drives[fdctrl->bootsel]; ! 611: } ! 612: ! 613: static inline fdrive_t *drv1 (fdctrl_t *fdctrl) ! 614: { ! 615: return &fdctrl->drives[1 - fdctrl->bootsel]; ! 616: } ! 617: ! 618: static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) ! 619: { ! 620: return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl); ! 621: } ! 622: ! 623: /* Status B register : 0x01 (read-only) */ ! 624: static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) ! 625: { ! 626: FLOPPY_DPRINTF("status register: 0x00\n"); ! 627: return 0; ! 628: } ! 629: ! 630: /* Digital output register : 0x02 */ ! 631: static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) ! 632: { ! 633: uint32_t retval = 0; ! 634: ! 635: /* Drive motors state indicators */ ! 636: if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON) ! 637: retval |= 1 << 5; ! 638: if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON) ! 639: retval |= 1 << 4; ! 640: /* DMA enable */ ! 641: retval |= fdctrl->dma_en << 3; ! 642: /* Reset indicator */ ! 643: retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0; ! 644: /* Selected drive */ ! 645: retval |= fdctrl->cur_drv; ! 646: FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval); ! 647: ! 648: return retval; ! 649: } ! 650: ! 651: static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) ! 652: { ! 653: /* Reset mode */ ! 654: if (fdctrl->state & FD_CTRL_RESET) { ! 655: if (!(value & 0x04)) { ! 656: FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); ! 657: return; ! 658: } ! 659: } ! 660: FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); ! 661: /* Drive motors state indicators */ ! 662: if (value & 0x20) ! 663: fd_start(drv1(fdctrl)); ! 664: else ! 665: fd_stop(drv1(fdctrl)); ! 666: if (value & 0x10) ! 667: fd_start(drv0(fdctrl)); ! 668: else ! 669: fd_stop(drv0(fdctrl)); ! 670: /* DMA enable */ ! 671: #if 0 ! 672: if (fdctrl->dma_chann != -1) ! 673: fdctrl->dma_en = 1 - ((value >> 3) & 1); ! 674: #endif ! 675: /* Reset */ ! 676: if (!(value & 0x04)) { ! 677: if (!(fdctrl->state & FD_CTRL_RESET)) { ! 678: FLOPPY_DPRINTF("controller enter RESET state\n"); ! 679: fdctrl->state |= FD_CTRL_RESET; ! 680: } ! 681: } else { ! 682: if (fdctrl->state & FD_CTRL_RESET) { ! 683: FLOPPY_DPRINTF("controller out of RESET state\n"); ! 684: fdctrl_reset(fdctrl, 1); ! 685: fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP); ! 686: } ! 687: } ! 688: /* Selected drive */ ! 689: fdctrl->cur_drv = value & 1; ! 690: } ! 691: ! 692: /* Tape drive register : 0x03 */ ! 693: static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) ! 694: { ! 695: uint32_t retval = 0; ! 696: ! 697: /* Disk boot selection indicator */ ! 698: retval |= fdctrl->bootsel << 2; ! 699: /* Tape indicators: never allowed */ ! 700: FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval); ! 701: ! 702: return retval; ! 703: } ! 704: ! 705: static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) ! 706: { ! 707: /* Reset mode */ ! 708: if (fdctrl->state & FD_CTRL_RESET) { ! 709: FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); ! 710: return; ! 711: } ! 712: FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value); ! 713: /* Disk boot selection indicator */ ! 714: fdctrl->bootsel = (value >> 2) & 1; ! 715: /* Tape indicators: never allow */ ! 716: } ! 717: ! 718: /* Main status register : 0x04 (read) */ ! 719: static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) ! 720: { ! 721: uint32_t retval = 0; ! 722: ! 723: fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET); ! 724: if (!(fdctrl->state & FD_CTRL_BUSY)) { ! 725: /* Data transfer allowed */ ! 726: retval |= 0x80; ! 727: /* Data transfer direction indicator */ ! 728: if (fdctrl->data_dir == FD_DIR_READ) ! 729: retval |= 0x40; ! 730: } ! 731: /* Should handle 0x20 for SPECIFY command */ ! 732: /* Command busy indicator */ ! 733: if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA || ! 734: FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) ! 735: retval |= 0x10; ! 736: FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); ! 737: ! 738: return retval; ! 739: } ! 740: ! 741: /* Data select rate register : 0x04 (write) */ ! 742: static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) ! 743: { ! 744: /* Reset mode */ ! 745: if (fdctrl->state & FD_CTRL_RESET) { ! 746: FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); ! 747: return; ! 748: } ! 749: FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value); ! 750: /* Reset: autoclear */ ! 751: if (value & 0x80) { ! 752: fdctrl->state |= FD_CTRL_RESET; ! 753: fdctrl_reset(fdctrl, 1); ! 754: fdctrl->state &= ~FD_CTRL_RESET; ! 755: } ! 756: if (value & 0x40) { ! 757: fdctrl->state |= FD_CTRL_SLEEP; ! 758: fdctrl_reset(fdctrl, 1); ! 759: } ! 760: // fdctrl.precomp = (value >> 2) & 0x07; ! 761: } ! 762: ! 763: /* Digital input register : 0x07 (read-only) */ ! 764: static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) ! 765: { ! 766: uint32_t retval = 0; ! 767: ! 768: if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE || ! 769: drv1(fdctrl)->drflags & FDRIVE_REVALIDATE) ! 770: retval |= 0x80; ! 771: if (retval != 0) ! 772: FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); ! 773: drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; ! 774: drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE; ! 775: ! 776: return retval; ! 777: } ! 778: ! 779: /* FIFO state control */ ! 780: static void fdctrl_reset_fifo (fdctrl_t *fdctrl) ! 781: { ! 782: fdctrl->data_dir = FD_DIR_WRITE; ! 783: fdctrl->data_pos = 0; ! 784: FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD); ! 785: } ! 786: ! 787: /* Set FIFO status for the host to read */ ! 788: static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq) ! 789: { ! 790: fdctrl->data_dir = FD_DIR_READ; ! 791: fdctrl->data_len = fifo_len; ! 792: fdctrl->data_pos = 0; ! 793: FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS); ! 794: if (do_irq) ! 795: fdctrl_raise_irq(fdctrl, 0x00); ! 796: } ! 797: ! 798: /* Set an error: unimplemented/unknown command */ ! 799: static void fdctrl_unimplemented (fdctrl_t *fdctrl) ! 800: { ! 801: #if 0 ! 802: fdrive_t *cur_drv; ! 803: ! 804: cur_drv = get_cur_drv(fdctrl); ! 805: fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv; ! 806: fdctrl->fifo[1] = 0x00; ! 807: fdctrl->fifo[2] = 0x00; ! 808: fdctrl_set_fifo(fdctrl, 3, 1); ! 809: #else ! 810: // fdctrl_reset_fifo(fdctrl); ! 811: fdctrl->fifo[0] = 0x80; ! 812: fdctrl_set_fifo(fdctrl, 1, 0); ! 813: #endif ! 814: } ! 815: ! 816: /* Callback for transfer end (stop or abort) */ ! 817: static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, ! 818: uint8_t status1, uint8_t status2) ! 819: { ! 820: fdrive_t *cur_drv; ! 821: ! 822: cur_drv = get_cur_drv(fdctrl); ! 823: FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", ! 824: status0, status1, status2, ! 825: status0 | (cur_drv->head << 2) | fdctrl->cur_drv); ! 826: fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv; ! 827: fdctrl->fifo[1] = status1; ! 828: fdctrl->fifo[2] = status2; ! 829: fdctrl->fifo[3] = cur_drv->track; ! 830: fdctrl->fifo[4] = cur_drv->head; ! 831: fdctrl->fifo[5] = cur_drv->sect; ! 832: fdctrl->fifo[6] = FD_SECTOR_SC; ! 833: fdctrl->data_dir = FD_DIR_READ; ! 834: if (fdctrl->state & FD_CTRL_BUSY) { ! 835: DMA_release_DREQ(fdctrl->dma_chann); ! 836: fdctrl->state &= ~FD_CTRL_BUSY; ! 837: } ! 838: fdctrl_set_fifo(fdctrl, 7, 1); ! 839: } ! 840: ! 841: /* Prepare a data transfer (either DMA or FIFO) */ ! 842: static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) ! 843: { ! 844: fdrive_t *cur_drv; ! 845: uint8_t kh, kt, ks; ! 846: int did_seek; ! 847: ! 848: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 849: cur_drv = get_cur_drv(fdctrl); ! 850: kt = fdctrl->fifo[2]; ! 851: kh = fdctrl->fifo[3]; ! 852: ks = fdctrl->fifo[4]; ! 853: FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", ! 854: fdctrl->cur_drv, kh, kt, ks, ! 855: _fd_sector(kh, kt, ks, cur_drv->last_sect)); ! 856: did_seek = 0; ! 857: switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { ! 858: case 2: ! 859: /* sect too big */ ! 860: fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); ! 861: fdctrl->fifo[3] = kt; ! 862: fdctrl->fifo[4] = kh; ! 863: fdctrl->fifo[5] = ks; ! 864: return; ! 865: case 3: ! 866: /* track too big */ ! 867: fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); ! 868: fdctrl->fifo[3] = kt; ! 869: fdctrl->fifo[4] = kh; ! 870: fdctrl->fifo[5] = ks; ! 871: return; ! 872: case 4: ! 873: /* No seek enabled */ ! 874: fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); ! 875: fdctrl->fifo[3] = kt; ! 876: fdctrl->fifo[4] = kh; ! 877: fdctrl->fifo[5] = ks; ! 878: return; ! 879: case 1: ! 880: did_seek = 1; ! 881: break; ! 882: default: ! 883: break; ! 884: } ! 885: /* Set the FIFO state */ ! 886: fdctrl->data_dir = direction; ! 887: fdctrl->data_pos = 0; ! 888: FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */ ! 889: if (fdctrl->fifo[0] & 0x80) ! 890: fdctrl->data_state |= FD_STATE_MULTI; ! 891: else ! 892: fdctrl->data_state &= ~FD_STATE_MULTI; ! 893: if (did_seek) ! 894: fdctrl->data_state |= FD_STATE_SEEK; ! 895: else ! 896: fdctrl->data_state &= ~FD_STATE_SEEK; ! 897: if (fdctrl->fifo[5] == 00) { ! 898: fdctrl->data_len = fdctrl->fifo[8]; ! 899: } else { ! 900: int tmp; ! 901: fdctrl->data_len = 128 << fdctrl->fifo[5]; ! 902: tmp = (cur_drv->last_sect - ks + 1); ! 903: if (fdctrl->fifo[0] & 0x80) ! 904: tmp += cur_drv->last_sect; ! 905: fdctrl->data_len *= tmp; ! 906: } ! 907: fdctrl->eot = fdctrl->fifo[6]; ! 908: if (fdctrl->dma_en) { ! 909: int dma_mode; ! 910: /* DMA transfer are enabled. Check if DMA channel is well programmed */ ! 911: dma_mode = DMA_get_channel_mode(fdctrl->dma_chann); ! 912: dma_mode = (dma_mode >> 2) & 3; ! 913: FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n", ! 914: dma_mode, direction, ! 915: (128 << fdctrl->fifo[5]) * ! 916: (cur_drv->last_sect - ks + 1), fdctrl->data_len); ! 917: if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL || ! 918: direction == FD_DIR_SCANH) && dma_mode == 0) || ! 919: (direction == FD_DIR_WRITE && dma_mode == 2) || ! 920: (direction == FD_DIR_READ && dma_mode == 1)) { ! 921: /* No access is allowed until DMA transfer has completed */ ! 922: fdctrl->state |= FD_CTRL_BUSY; ! 923: /* Now, we just have to wait for the DMA controller to ! 924: * recall us... ! 925: */ ! 926: DMA_hold_DREQ(fdctrl->dma_chann); ! 927: DMA_schedule(fdctrl->dma_chann); ! 928: return; ! 929: } else { ! 930: FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction); ! 931: } ! 932: } ! 933: FLOPPY_DPRINTF("start non-DMA transfer\n"); ! 934: /* IO based transfer: calculate len */ ! 935: fdctrl_raise_irq(fdctrl, 0x00); ! 936: ! 937: return; ! 938: } ! 939: ! 940: /* Prepare a transfer of deleted data */ ! 941: static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) ! 942: { ! 943: /* We don't handle deleted data, ! 944: * so we don't return *ANYTHING* ! 945: */ ! 946: fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); ! 947: } ! 948: ! 949: /* handlers for DMA transfers */ ! 950: static int fdctrl_transfer_handler (void *opaque, int nchan, ! 951: int dma_pos, int dma_len) ! 952: { ! 953: fdctrl_t *fdctrl; ! 954: fdrive_t *cur_drv; ! 955: int len, start_pos, rel_pos; ! 956: uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; ! 957: ! 958: fdctrl = opaque; ! 959: if (!(fdctrl->state & FD_CTRL_BUSY)) { ! 960: FLOPPY_DPRINTF("Not in DMA transfer mode !\n"); ! 961: return 0; ! 962: } ! 963: cur_drv = get_cur_drv(fdctrl); ! 964: if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL || ! 965: fdctrl->data_dir == FD_DIR_SCANH) ! 966: status2 = 0x04; ! 967: if (dma_len > fdctrl->data_len) ! 968: dma_len = fdctrl->data_len; ! 969: if (cur_drv->bs == NULL) { ! 970: if (fdctrl->data_dir == FD_DIR_WRITE) ! 971: fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); ! 972: else ! 973: fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); ! 974: len = 0; ! 975: goto transfer_error; ! 976: } ! 977: rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; ! 978: for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) { ! 979: len = dma_len - fdctrl->data_pos; ! 980: if (len + rel_pos > FD_SECTOR_LEN) ! 981: len = FD_SECTOR_LEN - rel_pos; ! 982: FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x " ! 983: "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos, ! 984: fdctrl->data_len, fdctrl->cur_drv, cur_drv->head, ! 985: cur_drv->track, cur_drv->sect, fd_sector(cur_drv), ! 986: fd_sector(cur_drv) * 512); ! 987: if (fdctrl->data_dir != FD_DIR_WRITE || ! 988: len < FD_SECTOR_LEN || rel_pos != 0) { ! 989: /* READ & SCAN commands and realign to a sector for WRITE */ ! 990: if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), ! 991: fdctrl->fifo, 1) < 0) { ! 992: FLOPPY_DPRINTF("Floppy: error getting sector %d\n", ! 993: fd_sector(cur_drv)); ! 994: /* Sure, image size is too small... */ ! 995: memset(fdctrl->fifo, 0, FD_SECTOR_LEN); ! 996: } ! 997: } ! 998: switch (fdctrl->data_dir) { ! 999: case FD_DIR_READ: ! 1000: /* READ commands */ ! 1001: DMA_write_memory (nchan, fdctrl->fifo + rel_pos, ! 1002: fdctrl->data_pos, len); ! 1003: /* cpu_physical_memory_write(addr + fdctrl->data_pos, */ ! 1004: /* fdctrl->fifo + rel_pos, len); */ ! 1005: break; ! 1006: case FD_DIR_WRITE: ! 1007: /* WRITE commands */ ! 1008: DMA_read_memory (nchan, fdctrl->fifo + rel_pos, ! 1009: fdctrl->data_pos, len); ! 1010: /* cpu_physical_memory_read(addr + fdctrl->data_pos, */ ! 1011: /* fdctrl->fifo + rel_pos, len); */ ! 1012: if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), ! 1013: fdctrl->fifo, 1) < 0) { ! 1014: FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv)); ! 1015: fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); ! 1016: goto transfer_error; ! 1017: } ! 1018: break; ! 1019: default: ! 1020: /* SCAN commands */ ! 1021: { ! 1022: uint8_t tmpbuf[FD_SECTOR_LEN]; ! 1023: int ret; ! 1024: DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len); ! 1025: /* cpu_physical_memory_read(addr + fdctrl->data_pos, */ ! 1026: /* tmpbuf, len); */ ! 1027: ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len); ! 1028: if (ret == 0) { ! 1029: status2 = 0x08; ! 1030: goto end_transfer; ! 1031: } ! 1032: if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) || ! 1033: (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) { ! 1034: status2 = 0x00; ! 1035: goto end_transfer; ! 1036: } ! 1037: } ! 1038: break; ! 1039: } ! 1040: fdctrl->data_pos += len; ! 1041: rel_pos = fdctrl->data_pos % FD_SECTOR_LEN; ! 1042: if (rel_pos == 0) { ! 1043: /* Seek to next sector */ ! 1044: FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n", ! 1045: cur_drv->head, cur_drv->track, cur_drv->sect, ! 1046: fd_sector(cur_drv), ! 1047: fdctrl->data_pos - len); ! 1048: /* XXX: cur_drv->sect >= cur_drv->last_sect should be an ! 1049: error in fact */ ! 1050: if (cur_drv->sect >= cur_drv->last_sect || ! 1051: cur_drv->sect == fdctrl->eot) { ! 1052: cur_drv->sect = 1; ! 1053: if (FD_MULTI_TRACK(fdctrl->data_state)) { ! 1054: if (cur_drv->head == 0 && ! 1055: (cur_drv->flags & FDISK_DBL_SIDES) != 0) { ! 1056: cur_drv->head = 1; ! 1057: } else { ! 1058: cur_drv->head = 0; ! 1059: cur_drv->track++; ! 1060: if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) ! 1061: break; ! 1062: } ! 1063: } else { ! 1064: cur_drv->track++; ! 1065: break; ! 1066: } ! 1067: FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n", ! 1068: cur_drv->head, cur_drv->track, ! 1069: cur_drv->sect, fd_sector(cur_drv)); ! 1070: } else { ! 1071: cur_drv->sect++; ! 1072: } ! 1073: } ! 1074: } ! 1075: end_transfer: ! 1076: len = fdctrl->data_pos - start_pos; ! 1077: FLOPPY_DPRINTF("end transfer %d %d %d\n", ! 1078: fdctrl->data_pos, len, fdctrl->data_len); ! 1079: if (fdctrl->data_dir == FD_DIR_SCANE || ! 1080: fdctrl->data_dir == FD_DIR_SCANL || ! 1081: fdctrl->data_dir == FD_DIR_SCANH) ! 1082: status2 = 0x08; ! 1083: if (FD_DID_SEEK(fdctrl->data_state)) ! 1084: status0 |= 0x20; ! 1085: fdctrl->data_len -= len; ! 1086: // if (fdctrl->data_len == 0) ! 1087: fdctrl_stop_transfer(fdctrl, status0, status1, status2); ! 1088: transfer_error: ! 1089: ! 1090: return len; ! 1091: } ! 1092: ! 1093: /* Data register : 0x05 */ ! 1094: static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) ! 1095: { ! 1096: fdrive_t *cur_drv; ! 1097: uint32_t retval = 0; ! 1098: int pos, len; ! 1099: ! 1100: cur_drv = get_cur_drv(fdctrl); ! 1101: fdctrl->state &= ~FD_CTRL_SLEEP; ! 1102: if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) { ! 1103: FLOPPY_ERROR("can't read data in CMD state\n"); ! 1104: return 0; ! 1105: } ! 1106: pos = fdctrl->data_pos; ! 1107: if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { ! 1108: pos %= FD_SECTOR_LEN; ! 1109: if (pos == 0) { ! 1110: len = fdctrl->data_len - fdctrl->data_pos; ! 1111: if (len > FD_SECTOR_LEN) ! 1112: len = FD_SECTOR_LEN; ! 1113: bdrv_read(cur_drv->bs, fd_sector(cur_drv), ! 1114: fdctrl->fifo, len); ! 1115: } ! 1116: } ! 1117: retval = fdctrl->fifo[pos]; ! 1118: if (++fdctrl->data_pos == fdctrl->data_len) { ! 1119: fdctrl->data_pos = 0; ! 1120: /* Switch from transfer mode to status mode ! 1121: * then from status mode to command mode ! 1122: */ ! 1123: if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { ! 1124: fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); ! 1125: } else { ! 1126: fdctrl_reset_fifo(fdctrl); ! 1127: fdctrl_reset_irq(fdctrl); ! 1128: } ! 1129: } ! 1130: FLOPPY_DPRINTF("data register: 0x%02x\n", retval); ! 1131: ! 1132: return retval; ! 1133: } ! 1134: ! 1135: static void fdctrl_format_sector (fdctrl_t *fdctrl) ! 1136: { ! 1137: fdrive_t *cur_drv; ! 1138: uint8_t kh, kt, ks; ! 1139: int did_seek; ! 1140: ! 1141: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1142: cur_drv = get_cur_drv(fdctrl); ! 1143: kt = fdctrl->fifo[6]; ! 1144: kh = fdctrl->fifo[7]; ! 1145: ks = fdctrl->fifo[8]; ! 1146: FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", ! 1147: fdctrl->cur_drv, kh, kt, ks, ! 1148: _fd_sector(kh, kt, ks, cur_drv->last_sect)); ! 1149: did_seek = 0; ! 1150: switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) { ! 1151: case 2: ! 1152: /* sect too big */ ! 1153: fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); ! 1154: fdctrl->fifo[3] = kt; ! 1155: fdctrl->fifo[4] = kh; ! 1156: fdctrl->fifo[5] = ks; ! 1157: return; ! 1158: case 3: ! 1159: /* track too big */ ! 1160: fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00); ! 1161: fdctrl->fifo[3] = kt; ! 1162: fdctrl->fifo[4] = kh; ! 1163: fdctrl->fifo[5] = ks; ! 1164: return; ! 1165: case 4: ! 1166: /* No seek enabled */ ! 1167: fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00); ! 1168: fdctrl->fifo[3] = kt; ! 1169: fdctrl->fifo[4] = kh; ! 1170: fdctrl->fifo[5] = ks; ! 1171: return; ! 1172: case 1: ! 1173: did_seek = 1; ! 1174: fdctrl->data_state |= FD_STATE_SEEK; ! 1175: break; ! 1176: default: ! 1177: break; ! 1178: } ! 1179: memset(fdctrl->fifo, 0, FD_SECTOR_LEN); ! 1180: if (cur_drv->bs == NULL || ! 1181: bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { ! 1182: FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv)); ! 1183: fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00); ! 1184: } else { ! 1185: if (cur_drv->sect == cur_drv->last_sect) { ! 1186: fdctrl->data_state &= ~FD_STATE_FORMAT; ! 1187: /* Last sector done */ ! 1188: if (FD_DID_SEEK(fdctrl->data_state)) ! 1189: fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); ! 1190: else ! 1191: fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); ! 1192: } else { ! 1193: /* More to do */ ! 1194: fdctrl->data_pos = 0; ! 1195: fdctrl->data_len = 4; ! 1196: } ! 1197: } ! 1198: } ! 1199: ! 1200: static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) ! 1201: { ! 1202: fdrive_t *cur_drv; ! 1203: ! 1204: cur_drv = get_cur_drv(fdctrl); ! 1205: /* Reset mode */ ! 1206: if (fdctrl->state & FD_CTRL_RESET) { ! 1207: FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); ! 1208: return; ! 1209: } ! 1210: fdctrl->state &= ~FD_CTRL_SLEEP; ! 1211: if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) { ! 1212: FLOPPY_ERROR("can't write data in status mode\n"); ! 1213: return; ! 1214: } ! 1215: /* Is it write command time ? */ ! 1216: if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) { ! 1217: /* FIFO data write */ ! 1218: fdctrl->fifo[fdctrl->data_pos++] = value; ! 1219: if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) || ! 1220: fdctrl->data_pos == fdctrl->data_len) { ! 1221: bdrv_write(cur_drv->bs, fd_sector(cur_drv), ! 1222: fdctrl->fifo, FD_SECTOR_LEN); ! 1223: } ! 1224: /* Switch from transfer mode to status mode ! 1225: * then from status mode to command mode ! 1226: */ ! 1227: if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) ! 1228: fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); ! 1229: return; ! 1230: } ! 1231: if (fdctrl->data_pos == 0) { ! 1232: /* Command */ ! 1233: switch (value & 0x5F) { ! 1234: case 0x46: ! 1235: /* READ variants */ ! 1236: FLOPPY_DPRINTF("READ command\n"); ! 1237: /* 8 parameters cmd */ ! 1238: fdctrl->data_len = 9; ! 1239: goto enqueue; ! 1240: case 0x4C: ! 1241: /* READ_DELETED variants */ ! 1242: FLOPPY_DPRINTF("READ_DELETED command\n"); ! 1243: /* 8 parameters cmd */ ! 1244: fdctrl->data_len = 9; ! 1245: goto enqueue; ! 1246: case 0x50: ! 1247: /* SCAN_EQUAL variants */ ! 1248: FLOPPY_DPRINTF("SCAN_EQUAL command\n"); ! 1249: /* 8 parameters cmd */ ! 1250: fdctrl->data_len = 9; ! 1251: goto enqueue; ! 1252: case 0x56: ! 1253: /* VERIFY variants */ ! 1254: FLOPPY_DPRINTF("VERIFY command\n"); ! 1255: /* 8 parameters cmd */ ! 1256: fdctrl->data_len = 9; ! 1257: goto enqueue; ! 1258: case 0x59: ! 1259: /* SCAN_LOW_OR_EQUAL variants */ ! 1260: FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n"); ! 1261: /* 8 parameters cmd */ ! 1262: fdctrl->data_len = 9; ! 1263: goto enqueue; ! 1264: case 0x5D: ! 1265: /* SCAN_HIGH_OR_EQUAL variants */ ! 1266: FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n"); ! 1267: /* 8 parameters cmd */ ! 1268: fdctrl->data_len = 9; ! 1269: goto enqueue; ! 1270: default: ! 1271: break; ! 1272: } ! 1273: switch (value & 0x7F) { ! 1274: case 0x45: ! 1275: /* WRITE variants */ ! 1276: FLOPPY_DPRINTF("WRITE command\n"); ! 1277: /* 8 parameters cmd */ ! 1278: fdctrl->data_len = 9; ! 1279: goto enqueue; ! 1280: case 0x49: ! 1281: /* WRITE_DELETED variants */ ! 1282: FLOPPY_DPRINTF("WRITE_DELETED command\n"); ! 1283: /* 8 parameters cmd */ ! 1284: fdctrl->data_len = 9; ! 1285: goto enqueue; ! 1286: default: ! 1287: break; ! 1288: } ! 1289: switch (value) { ! 1290: case 0x03: ! 1291: /* SPECIFY */ ! 1292: FLOPPY_DPRINTF("SPECIFY command\n"); ! 1293: /* 1 parameter cmd */ ! 1294: fdctrl->data_len = 3; ! 1295: goto enqueue; ! 1296: case 0x04: ! 1297: /* SENSE_DRIVE_STATUS */ ! 1298: FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n"); ! 1299: /* 1 parameter cmd */ ! 1300: fdctrl->data_len = 2; ! 1301: goto enqueue; ! 1302: case 0x07: ! 1303: /* RECALIBRATE */ ! 1304: FLOPPY_DPRINTF("RECALIBRATE command\n"); ! 1305: /* 1 parameter cmd */ ! 1306: fdctrl->data_len = 2; ! 1307: goto enqueue; ! 1308: case 0x08: ! 1309: /* SENSE_INTERRUPT_STATUS */ ! 1310: FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n", ! 1311: fdctrl->int_status); ! 1312: /* No parameters cmd: returns status if no interrupt */ ! 1313: #if 0 ! 1314: fdctrl->fifo[0] = ! 1315: fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv; ! 1316: #else ! 1317: /* XXX: int_status handling is broken for read/write ! 1318: commands, so we do this hack. It should be suppressed ! 1319: ASAP */ ! 1320: fdctrl->fifo[0] = ! 1321: 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv; ! 1322: #endif ! 1323: fdctrl->fifo[1] = cur_drv->track; ! 1324: fdctrl_set_fifo(fdctrl, 2, 0); ! 1325: fdctrl_reset_irq(fdctrl); ! 1326: fdctrl->int_status = 0xC0; ! 1327: return; ! 1328: case 0x0E: ! 1329: /* DUMPREG */ ! 1330: FLOPPY_DPRINTF("DUMPREG command\n"); ! 1331: /* Drives position */ ! 1332: fdctrl->fifo[0] = drv0(fdctrl)->track; ! 1333: fdctrl->fifo[1] = drv1(fdctrl)->track; ! 1334: fdctrl->fifo[2] = 0; ! 1335: fdctrl->fifo[3] = 0; ! 1336: /* timers */ ! 1337: fdctrl->fifo[4] = fdctrl->timer0; ! 1338: fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en; ! 1339: fdctrl->fifo[6] = cur_drv->last_sect; ! 1340: fdctrl->fifo[7] = (fdctrl->lock << 7) | ! 1341: (cur_drv->perpendicular << 2); ! 1342: fdctrl->fifo[8] = fdctrl->config; ! 1343: fdctrl->fifo[9] = fdctrl->precomp_trk; ! 1344: fdctrl_set_fifo(fdctrl, 10, 0); ! 1345: return; ! 1346: case 0x0F: ! 1347: /* SEEK */ ! 1348: FLOPPY_DPRINTF("SEEK command\n"); ! 1349: /* 2 parameters cmd */ ! 1350: fdctrl->data_len = 3; ! 1351: goto enqueue; ! 1352: case 0x10: ! 1353: /* VERSION */ ! 1354: FLOPPY_DPRINTF("VERSION command\n"); ! 1355: /* No parameters cmd */ ! 1356: /* Controller's version */ ! 1357: fdctrl->fifo[0] = fdctrl->version; ! 1358: fdctrl_set_fifo(fdctrl, 1, 1); ! 1359: return; ! 1360: case 0x12: ! 1361: /* PERPENDICULAR_MODE */ ! 1362: FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n"); ! 1363: /* 1 parameter cmd */ ! 1364: fdctrl->data_len = 2; ! 1365: goto enqueue; ! 1366: case 0x13: ! 1367: /* CONFIGURE */ ! 1368: FLOPPY_DPRINTF("CONFIGURE command\n"); ! 1369: /* 3 parameters cmd */ ! 1370: fdctrl->data_len = 4; ! 1371: goto enqueue; ! 1372: case 0x14: ! 1373: /* UNLOCK */ ! 1374: FLOPPY_DPRINTF("UNLOCK command\n"); ! 1375: /* No parameters cmd */ ! 1376: fdctrl->lock = 0; ! 1377: fdctrl->fifo[0] = 0; ! 1378: fdctrl_set_fifo(fdctrl, 1, 0); ! 1379: return; ! 1380: case 0x17: ! 1381: /* POWERDOWN_MODE */ ! 1382: FLOPPY_DPRINTF("POWERDOWN_MODE command\n"); ! 1383: /* 2 parameters cmd */ ! 1384: fdctrl->data_len = 3; ! 1385: goto enqueue; ! 1386: case 0x18: ! 1387: /* PART_ID */ ! 1388: FLOPPY_DPRINTF("PART_ID command\n"); ! 1389: /* No parameters cmd */ ! 1390: fdctrl->fifo[0] = 0x41; /* Stepping 1 */ ! 1391: fdctrl_set_fifo(fdctrl, 1, 0); ! 1392: return; ! 1393: case 0x2C: ! 1394: /* SAVE */ ! 1395: FLOPPY_DPRINTF("SAVE command\n"); ! 1396: /* No parameters cmd */ ! 1397: fdctrl->fifo[0] = 0; ! 1398: fdctrl->fifo[1] = 0; ! 1399: /* Drives position */ ! 1400: fdctrl->fifo[2] = drv0(fdctrl)->track; ! 1401: fdctrl->fifo[3] = drv1(fdctrl)->track; ! 1402: fdctrl->fifo[4] = 0; ! 1403: fdctrl->fifo[5] = 0; ! 1404: /* timers */ ! 1405: fdctrl->fifo[6] = fdctrl->timer0; ! 1406: fdctrl->fifo[7] = fdctrl->timer1; ! 1407: fdctrl->fifo[8] = cur_drv->last_sect; ! 1408: fdctrl->fifo[9] = (fdctrl->lock << 7) | ! 1409: (cur_drv->perpendicular << 2); ! 1410: fdctrl->fifo[10] = fdctrl->config; ! 1411: fdctrl->fifo[11] = fdctrl->precomp_trk; ! 1412: fdctrl->fifo[12] = fdctrl->pwrd; ! 1413: fdctrl->fifo[13] = 0; ! 1414: fdctrl->fifo[14] = 0; ! 1415: fdctrl_set_fifo(fdctrl, 15, 1); ! 1416: return; ! 1417: case 0x33: ! 1418: /* OPTION */ ! 1419: FLOPPY_DPRINTF("OPTION command\n"); ! 1420: /* 1 parameter cmd */ ! 1421: fdctrl->data_len = 2; ! 1422: goto enqueue; ! 1423: case 0x42: ! 1424: /* READ_TRACK */ ! 1425: FLOPPY_DPRINTF("READ_TRACK command\n"); ! 1426: /* 8 parameters cmd */ ! 1427: fdctrl->data_len = 9; ! 1428: goto enqueue; ! 1429: case 0x4A: ! 1430: /* READ_ID */ ! 1431: FLOPPY_DPRINTF("READ_ID command\n"); ! 1432: /* 1 parameter cmd */ ! 1433: fdctrl->data_len = 2; ! 1434: goto enqueue; ! 1435: case 0x4C: ! 1436: /* RESTORE */ ! 1437: FLOPPY_DPRINTF("RESTORE command\n"); ! 1438: /* 17 parameters cmd */ ! 1439: fdctrl->data_len = 18; ! 1440: goto enqueue; ! 1441: case 0x4D: ! 1442: /* FORMAT_TRACK */ ! 1443: FLOPPY_DPRINTF("FORMAT_TRACK command\n"); ! 1444: /* 5 parameters cmd */ ! 1445: fdctrl->data_len = 6; ! 1446: goto enqueue; ! 1447: case 0x8E: ! 1448: /* DRIVE_SPECIFICATION_COMMAND */ ! 1449: FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n"); ! 1450: /* 5 parameters cmd */ ! 1451: fdctrl->data_len = 6; ! 1452: goto enqueue; ! 1453: case 0x8F: ! 1454: /* RELATIVE_SEEK_OUT */ ! 1455: FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n"); ! 1456: /* 2 parameters cmd */ ! 1457: fdctrl->data_len = 3; ! 1458: goto enqueue; ! 1459: case 0x94: ! 1460: /* LOCK */ ! 1461: FLOPPY_DPRINTF("LOCK command\n"); ! 1462: /* No parameters cmd */ ! 1463: fdctrl->lock = 1; ! 1464: fdctrl->fifo[0] = 0x10; ! 1465: fdctrl_set_fifo(fdctrl, 1, 1); ! 1466: return; ! 1467: case 0xCD: ! 1468: /* FORMAT_AND_WRITE */ ! 1469: FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n"); ! 1470: /* 10 parameters cmd */ ! 1471: fdctrl->data_len = 11; ! 1472: goto enqueue; ! 1473: case 0xCF: ! 1474: /* RELATIVE_SEEK_IN */ ! 1475: FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n"); ! 1476: /* 2 parameters cmd */ ! 1477: fdctrl->data_len = 3; ! 1478: goto enqueue; ! 1479: default: ! 1480: /* Unknown command */ ! 1481: FLOPPY_ERROR("unknown command: 0x%02x\n", value); ! 1482: fdctrl_unimplemented(fdctrl); ! 1483: return; ! 1484: } ! 1485: } ! 1486: enqueue: ! 1487: FLOPPY_DPRINTF("%s: %02x\n", __func__, value); ! 1488: fdctrl->fifo[fdctrl->data_pos] = value; ! 1489: if (++fdctrl->data_pos == fdctrl->data_len) { ! 1490: /* We now have all parameters ! 1491: * and will be able to treat the command ! 1492: */ ! 1493: if (fdctrl->data_state & FD_STATE_FORMAT) { ! 1494: fdctrl_format_sector(fdctrl); ! 1495: return; ! 1496: } ! 1497: switch (fdctrl->fifo[0] & 0x1F) { ! 1498: case 0x06: ! 1499: { ! 1500: /* READ variants */ ! 1501: FLOPPY_DPRINTF("treat READ command\n"); ! 1502: fdctrl_start_transfer(fdctrl, FD_DIR_READ); ! 1503: return; ! 1504: } ! 1505: case 0x0C: ! 1506: /* READ_DELETED variants */ ! 1507: // FLOPPY_DPRINTF("treat READ_DELETED command\n"); ! 1508: FLOPPY_ERROR("treat READ_DELETED command\n"); ! 1509: fdctrl_start_transfer_del(fdctrl, FD_DIR_READ); ! 1510: return; ! 1511: case 0x16: ! 1512: /* VERIFY variants */ ! 1513: // FLOPPY_DPRINTF("treat VERIFY command\n"); ! 1514: FLOPPY_ERROR("treat VERIFY command\n"); ! 1515: fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00); ! 1516: return; ! 1517: case 0x10: ! 1518: /* SCAN_EQUAL variants */ ! 1519: // FLOPPY_DPRINTF("treat SCAN_EQUAL command\n"); ! 1520: FLOPPY_ERROR("treat SCAN_EQUAL command\n"); ! 1521: fdctrl_start_transfer(fdctrl, FD_DIR_SCANE); ! 1522: return; ! 1523: case 0x19: ! 1524: /* SCAN_LOW_OR_EQUAL variants */ ! 1525: // FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n"); ! 1526: FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n"); ! 1527: fdctrl_start_transfer(fdctrl, FD_DIR_SCANL); ! 1528: return; ! 1529: case 0x1D: ! 1530: /* SCAN_HIGH_OR_EQUAL variants */ ! 1531: // FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n"); ! 1532: FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n"); ! 1533: fdctrl_start_transfer(fdctrl, FD_DIR_SCANH); ! 1534: return; ! 1535: default: ! 1536: break; ! 1537: } ! 1538: switch (fdctrl->fifo[0] & 0x3F) { ! 1539: case 0x05: ! 1540: /* WRITE variants */ ! 1541: FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]); ! 1542: fdctrl_start_transfer(fdctrl, FD_DIR_WRITE); ! 1543: return; ! 1544: case 0x09: ! 1545: /* WRITE_DELETED variants */ ! 1546: // FLOPPY_DPRINTF("treat WRITE_DELETED command\n"); ! 1547: FLOPPY_ERROR("treat WRITE_DELETED command\n"); ! 1548: fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE); ! 1549: return; ! 1550: default: ! 1551: break; ! 1552: } ! 1553: switch (fdctrl->fifo[0]) { ! 1554: case 0x03: ! 1555: /* SPECIFY */ ! 1556: FLOPPY_DPRINTF("treat SPECIFY command\n"); ! 1557: fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; ! 1558: fdctrl->timer1 = fdctrl->fifo[2] >> 1; ! 1559: fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ; ! 1560: /* No result back */ ! 1561: fdctrl_reset_fifo(fdctrl); ! 1562: break; ! 1563: case 0x04: ! 1564: /* SENSE_DRIVE_STATUS */ ! 1565: FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n"); ! 1566: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1567: cur_drv = get_cur_drv(fdctrl); ! 1568: cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; ! 1569: /* 1 Byte status back */ ! 1570: fdctrl->fifo[0] = (cur_drv->ro << 6) | ! 1571: (cur_drv->track == 0 ? 0x10 : 0x00) | ! 1572: (cur_drv->head << 2) | ! 1573: fdctrl->cur_drv | ! 1574: 0x28; ! 1575: fdctrl_set_fifo(fdctrl, 1, 0); ! 1576: break; ! 1577: case 0x07: ! 1578: /* RECALIBRATE */ ! 1579: FLOPPY_DPRINTF("treat RECALIBRATE command\n"); ! 1580: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1581: cur_drv = get_cur_drv(fdctrl); ! 1582: fd_recalibrate(cur_drv); ! 1583: fdctrl_reset_fifo(fdctrl); ! 1584: /* Raise Interrupt */ ! 1585: fdctrl_raise_irq(fdctrl, 0x20); ! 1586: break; ! 1587: case 0x0F: ! 1588: /* SEEK */ ! 1589: FLOPPY_DPRINTF("treat SEEK command\n"); ! 1590: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1591: cur_drv = get_cur_drv(fdctrl); ! 1592: fd_start(cur_drv); ! 1593: if (fdctrl->fifo[2] <= cur_drv->track) ! 1594: cur_drv->dir = 1; ! 1595: else ! 1596: cur_drv->dir = 0; ! 1597: fdctrl_reset_fifo(fdctrl); ! 1598: if (fdctrl->fifo[2] > cur_drv->max_track) { ! 1599: fdctrl_raise_irq(fdctrl, 0x60); ! 1600: } else { ! 1601: cur_drv->track = fdctrl->fifo[2]; ! 1602: /* Raise Interrupt */ ! 1603: fdctrl_raise_irq(fdctrl, 0x20); ! 1604: } ! 1605: break; ! 1606: case 0x12: ! 1607: /* PERPENDICULAR_MODE */ ! 1608: FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n"); ! 1609: if (fdctrl->fifo[1] & 0x80) ! 1610: cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; ! 1611: /* No result back */ ! 1612: fdctrl_reset_fifo(fdctrl); ! 1613: break; ! 1614: case 0x13: ! 1615: /* CONFIGURE */ ! 1616: FLOPPY_DPRINTF("treat CONFIGURE command\n"); ! 1617: fdctrl->config = fdctrl->fifo[2]; ! 1618: fdctrl->precomp_trk = fdctrl->fifo[3]; ! 1619: /* No result back */ ! 1620: fdctrl_reset_fifo(fdctrl); ! 1621: break; ! 1622: case 0x17: ! 1623: /* POWERDOWN_MODE */ ! 1624: FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n"); ! 1625: fdctrl->pwrd = fdctrl->fifo[1]; ! 1626: fdctrl->fifo[0] = fdctrl->fifo[1]; ! 1627: fdctrl_set_fifo(fdctrl, 1, 1); ! 1628: break; ! 1629: case 0x33: ! 1630: /* OPTION */ ! 1631: FLOPPY_DPRINTF("treat OPTION command\n"); ! 1632: /* No result back */ ! 1633: fdctrl_reset_fifo(fdctrl); ! 1634: break; ! 1635: case 0x42: ! 1636: /* READ_TRACK */ ! 1637: // FLOPPY_DPRINTF("treat READ_TRACK command\n"); ! 1638: FLOPPY_ERROR("treat READ_TRACK command\n"); ! 1639: fdctrl_start_transfer(fdctrl, FD_DIR_READ); ! 1640: break; ! 1641: case 0x4A: ! 1642: /* READ_ID */ ! 1643: FLOPPY_DPRINTF("treat READ_ID command\n"); ! 1644: /* XXX: should set main status register to busy */ ! 1645: cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; ! 1646: qemu_mod_timer(fdctrl->result_timer, ! 1647: qemu_get_clock(vm_clock) + (ticks_per_sec / 50)); ! 1648: break; ! 1649: case 0x4C: ! 1650: /* RESTORE */ ! 1651: FLOPPY_DPRINTF("treat RESTORE command\n"); ! 1652: /* Drives position */ ! 1653: drv0(fdctrl)->track = fdctrl->fifo[3]; ! 1654: drv1(fdctrl)->track = fdctrl->fifo[4]; ! 1655: /* timers */ ! 1656: fdctrl->timer0 = fdctrl->fifo[7]; ! 1657: fdctrl->timer1 = fdctrl->fifo[8]; ! 1658: cur_drv->last_sect = fdctrl->fifo[9]; ! 1659: fdctrl->lock = fdctrl->fifo[10] >> 7; ! 1660: cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF; ! 1661: fdctrl->config = fdctrl->fifo[11]; ! 1662: fdctrl->precomp_trk = fdctrl->fifo[12]; ! 1663: fdctrl->pwrd = fdctrl->fifo[13]; ! 1664: fdctrl_reset_fifo(fdctrl); ! 1665: break; ! 1666: case 0x4D: ! 1667: /* FORMAT_TRACK */ ! 1668: FLOPPY_DPRINTF("treat FORMAT_TRACK command\n"); ! 1669: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1670: cur_drv = get_cur_drv(fdctrl); ! 1671: fdctrl->data_state |= FD_STATE_FORMAT; ! 1672: if (fdctrl->fifo[0] & 0x80) ! 1673: fdctrl->data_state |= FD_STATE_MULTI; ! 1674: else ! 1675: fdctrl->data_state &= ~FD_STATE_MULTI; ! 1676: fdctrl->data_state &= ~FD_STATE_SEEK; ! 1677: cur_drv->bps = ! 1678: fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2]; ! 1679: #if 0 ! 1680: cur_drv->last_sect = ! 1681: cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] : ! 1682: fdctrl->fifo[3] / 2; ! 1683: #else ! 1684: cur_drv->last_sect = fdctrl->fifo[3]; ! 1685: #endif ! 1686: /* Bochs BIOS is buggy and don't send format informations ! 1687: * for each sector. So, pretend all's done right now... ! 1688: */ ! 1689: fdctrl->data_state &= ~FD_STATE_FORMAT; ! 1690: fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); ! 1691: break; ! 1692: case 0x8E: ! 1693: /* DRIVE_SPECIFICATION_COMMAND */ ! 1694: FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n"); ! 1695: if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { ! 1696: /* Command parameters done */ ! 1697: if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { ! 1698: fdctrl->fifo[0] = fdctrl->fifo[1]; ! 1699: fdctrl->fifo[2] = 0; ! 1700: fdctrl->fifo[3] = 0; ! 1701: fdctrl_set_fifo(fdctrl, 4, 1); ! 1702: } else { ! 1703: fdctrl_reset_fifo(fdctrl); ! 1704: } ! 1705: } else if (fdctrl->data_len > 7) { ! 1706: /* ERROR */ ! 1707: fdctrl->fifo[0] = 0x80 | ! 1708: (cur_drv->head << 2) | fdctrl->cur_drv; ! 1709: fdctrl_set_fifo(fdctrl, 1, 1); ! 1710: } ! 1711: break; ! 1712: case 0x8F: ! 1713: /* RELATIVE_SEEK_OUT */ ! 1714: FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n"); ! 1715: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1716: cur_drv = get_cur_drv(fdctrl); ! 1717: fd_start(cur_drv); ! 1718: cur_drv->dir = 0; ! 1719: if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) { ! 1720: cur_drv->track = cur_drv->max_track - 1; ! 1721: } else { ! 1722: cur_drv->track += fdctrl->fifo[2]; ! 1723: } ! 1724: fdctrl_reset_fifo(fdctrl); ! 1725: fdctrl_raise_irq(fdctrl, 0x20); ! 1726: break; ! 1727: case 0xCD: ! 1728: /* FORMAT_AND_WRITE */ ! 1729: // FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n"); ! 1730: FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n"); ! 1731: fdctrl_unimplemented(fdctrl); ! 1732: break; ! 1733: case 0xCF: ! 1734: /* RELATIVE_SEEK_IN */ ! 1735: FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n"); ! 1736: fdctrl->cur_drv = fdctrl->fifo[1] & 1; ! 1737: cur_drv = get_cur_drv(fdctrl); ! 1738: fd_start(cur_drv); ! 1739: cur_drv->dir = 1; ! 1740: if (fdctrl->fifo[2] > cur_drv->track) { ! 1741: cur_drv->track = 0; ! 1742: } else { ! 1743: cur_drv->track -= fdctrl->fifo[2]; ! 1744: } ! 1745: fdctrl_reset_fifo(fdctrl); ! 1746: /* Raise Interrupt */ ! 1747: fdctrl_raise_irq(fdctrl, 0x20); ! 1748: break; ! 1749: } ! 1750: } ! 1751: } ! 1752: ! 1753: static void fdctrl_result_timer(void *opaque) ! 1754: { ! 1755: fdctrl_t *fdctrl = opaque; ! 1756: fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); ! 1757: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.