fdc.c 68.8 KB
Newer Older
B
bellard 已提交
1
/*
2
 * QEMU Floppy disk emulator (Intel 82078)
3
 *
4
 * Copyright (c) 2003, 2007 Jocelyn Mayer
5
 * Copyright (c) 2008 Hervé Poussineau
6
 *
B
bellard 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * 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.
 */
B
bellard 已提交
25 26 27 28
/*
 * The controller is used in Sun4m systems in a slightly different
 * way. There are changes in DOR register and DMA is not available.
 */
B
Blue Swirl 已提交
29

30
#include "hw/hw.h"
P
Paolo Bonzini 已提交
31
#include "hw/block/fdc.h"
32 33
#include "qemu/error-report.h"
#include "qemu/timer.h"
P
Paolo Bonzini 已提交
34
#include "hw/isa/isa.h"
35 36
#include "hw/sysbus.h"
#include "hw/qdev-addr.h"
37 38
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
39
#include "qemu/log.h"
B
bellard 已提交
40 41 42 43 44 45

/********************************************************/
/* debug Floppy devices */
//#define DEBUG_FLOPPY

#ifdef DEBUG_FLOPPY
46 47
#define FLOPPY_DPRINTF(fmt, ...)                                \
    do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
B
bellard 已提交
48
#else
49
#define FLOPPY_DPRINTF(fmt, ...)
B
bellard 已提交
50 51 52 53 54
#endif

/********************************************************/
/* Floppy drive emulation                               */

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
typedef enum FDriveRate {
    FDRIVE_RATE_500K = 0x00,  /* 500 Kbps */
    FDRIVE_RATE_300K = 0x01,  /* 300 Kbps */
    FDRIVE_RATE_250K = 0x02,  /* 250 Kbps */
    FDRIVE_RATE_1M   = 0x03,  /*   1 Mbps */
} FDriveRate;

typedef struct FDFormat {
    FDriveType drive;
    uint8_t last_sect;
    uint8_t max_track;
    uint8_t max_head;
    FDriveRate rate;
} FDFormat;

static const FDFormat fd_formats[] = {
    /* First entry is default format */
    /* 1.44 MB 3"1/2 floppy disks */
    { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, },
    /* 2.88 MB 3"1/2 floppy disks */
    { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, },
    { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, },
    { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, },
    { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, },
    { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, },
    /* 720 kB 3"1/2 floppy disks */
    { FDRIVE_DRV_144,  9, 80, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, },
    /* 1.2 MB 5"1/4 floppy disks */
    { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, },
    { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, },
    /* 720 kB 5"1/4 floppy disks */
    { FDRIVE_DRV_120,  9, 80, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, },
    /* 360 kB 5"1/4 floppy disks */
    { FDRIVE_DRV_120,  9, 40, 1, FDRIVE_RATE_300K, },
    { FDRIVE_DRV_120,  9, 40, 0, FDRIVE_RATE_300K, },
    { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, },
    { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, },
    /* 320 kB 5"1/4 floppy disks */
    { FDRIVE_DRV_120,  8, 40, 1, FDRIVE_RATE_250K, },
    { FDRIVE_DRV_120,  8, 40, 0, FDRIVE_RATE_250K, },
    /* 360 kB must match 5"1/4 better than 3"1/2... */
    { FDRIVE_DRV_144,  9, 80, 0, FDRIVE_RATE_250K, },
    /* end */
    { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
};

static void pick_geometry(BlockDriverState *bs, int *nb_heads,
                          int *max_track, int *last_sect,
                          FDriveType drive_in, FDriveType *drive,
                          FDriveRate *rate)
{
    const FDFormat *parse;
    uint64_t nb_sectors, size;
    int i, first_match, match;

    bdrv_get_geometry(bs, &nb_sectors);
    match = -1;
    first_match = -1;
    for (i = 0; ; i++) {
        parse = &fd_formats[i];
        if (parse->drive == FDRIVE_DRV_NONE) {
            break;
        }
        if (drive_in == parse->drive ||
            drive_in == 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;
    *drive = parse->drive;
    *rate = parse->rate;
}

B
blueswir1 已提交
162 163 164
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))

B
bellard 已提交
165
/* Will always be a fixed parameter for us */
166 167 168
#define FD_SECTOR_LEN          512
#define FD_SECTOR_SC           2   /* Sector size code */
#define FD_RESET_SENSEI_COUNT  4   /* Number of sense interrupts on RESET */
B
bellard 已提交
169

170 171
typedef struct FDCtrl FDCtrl;

B
bellard 已提交
172
/* Floppy disk drive emulation */
B
Blue Swirl 已提交
173
typedef enum FDiskFlags {
174
    FDISK_DBL_SIDES  = 0x01,
B
Blue Swirl 已提交
175
} FDiskFlags;
176

B
Blue Swirl 已提交
177
typedef struct FDrive {
178
    FDCtrl *fdctrl;
B
bellard 已提交
179 180
    BlockDriverState *bs;
    /* Drive status */
B
Blue Swirl 已提交
181
    FDriveType drive;
B
bellard 已提交
182 183 184 185 186 187
    uint8_t perpendicular;    /* 2.88 MB access mode    */
    /* Position */
    uint8_t head;
    uint8_t track;
    uint8_t sect;
    /* Media */
B
Blue Swirl 已提交
188
    FDiskFlags flags;
B
bellard 已提交
189 190
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
191
    uint16_t bps;             /* Bytes per sector       */
B
bellard 已提交
192
    uint8_t ro;               /* Is read-only           */
193
    uint8_t media_changed;    /* Is media changed       */
194
    uint8_t media_rate;       /* Data rate of medium    */
B
Blue Swirl 已提交
195
} FDrive;
B
bellard 已提交
196

B
Blue Swirl 已提交
197
static void fd_init(FDrive *drv)
B
bellard 已提交
198 199
{
    /* Drive */
B
bellard 已提交
200
    drv->drive = FDRIVE_DRV_NONE;
B
bellard 已提交
201 202
    drv->perpendicular = 0;
    /* Disk */
203
    drv->last_sect = 0;
B
bellard 已提交
204 205 206
    drv->max_track = 0;
}

207 208
#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)

B
Blue Swirl 已提交
209
static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
210
                          uint8_t last_sect, uint8_t num_sides)
B
bellard 已提交
211
{
212
    return (((track * num_sides) + head) * last_sect) + sect - 1;
B
bellard 已提交
213 214 215
}

/* Returns current position, in sectors, for given drive */
B
Blue Swirl 已提交
216
static int fd_sector(FDrive *drv)
B
bellard 已提交
217
{
218 219
    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
                          NUM_SIDES(drv));
B
bellard 已提交
220 221
}

B
blueswir1 已提交
222 223 224 225 226 227 228
/* Seek to a new position:
 * returns 0 if already on right track
 * returns 1 if track changed
 * returns 2 if track is invalid
 * returns 3 if sector is invalid
 * returns 4 if seek is disabled
 */
B
Blue Swirl 已提交
229 230
static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
                   int enable_seek)
B
bellard 已提交
231 232
{
    uint32_t sector;
233 234 235
    int ret;

    if (track > drv->max_track ||
236
        (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
237 238 239 240
        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);
B
bellard 已提交
241 242 243
        return 2;
    }
    if (sect > drv->last_sect) {
244 245 246 247
        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);
B
bellard 已提交
248 249
        return 3;
    }
250
    sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
251
    ret = 0;
B
bellard 已提交
252 253 254
    if (sector != fd_sector(drv)) {
#if 0
        if (!enable_seek) {
B
Blue Swirl 已提交
255 256 257 258
            FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x"
                           " (max=%d %02x %02x)\n",
                           head, track, sect, 1, drv->max_track,
                           drv->last_sect);
B
bellard 已提交
259 260 261 262
            return 4;
        }
#endif
        drv->head = head;
263 264 265 266
        if (drv->track != track) {
            if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
                drv->media_changed = 0;
            }
267
            ret = 1;
268
        }
B
bellard 已提交
269 270 271 272
        drv->track = track;
        drv->sect = sect;
    }

273 274 275 276
    if (drv->bs == NULL || !bdrv_is_inserted(drv->bs)) {
        ret = 2;
    }

277
    return ret;
B
bellard 已提交
278 279 280
}

/* Set drive back to track 0 */
B
Blue Swirl 已提交
281
static void fd_recalibrate(FDrive *drv)
B
bellard 已提交
282 283
{
    FLOPPY_DPRINTF("recalibrate\n");
284
    fd_seek(drv, 0, 0, 1, 1);
B
bellard 已提交
285 286 287
}

/* Revalidate a disk drive after a disk change */
B
Blue Swirl 已提交
288
static void fd_revalidate(FDrive *drv)
B
bellard 已提交
289
{
290
    int nb_heads, max_track, last_sect, ro;
291
    FDriveType drive;
292
    FDriveRate rate;
B
bellard 已提交
293 294

    FLOPPY_DPRINTF("revalidate\n");
P
Pavel Hrdina 已提交
295
    if (drv->bs != NULL) {
296
        ro = bdrv_is_read_only(drv->bs);
297 298
        pick_geometry(drv->bs, &nb_heads, &max_track,
                      &last_sect, drv->drive, &drive, &rate);
P
Pavel Hrdina 已提交
299 300
        if (!bdrv_is_inserted(drv->bs)) {
            FLOPPY_DPRINTF("No disk in drive\n");
301
        } else {
302 303
            FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
                           max_track, last_sect, ro ? "ro" : "rw");
304 305 306 307 308 309 310 311 312
        }
        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;
313
        drv->drive = drive;
314
        drv->media_rate = rate;
B
bellard 已提交
315
    } else {
P
Pavel Hrdina 已提交
316
        FLOPPY_DPRINTF("No drive connected\n");
317
        drv->last_sect = 0;
318 319
        drv->max_track = 0;
        drv->flags &= ~FDISK_DBL_SIDES;
B
bellard 已提交
320
    }
321 322
}

B
bellard 已提交
323
/********************************************************/
B
bellard 已提交
324
/* Intel 82078 floppy disk controller emulation          */
B
bellard 已提交
325

B
Blue Swirl 已提交
326 327
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
static void fdctrl_reset_fifo(FDCtrl *fdctrl);
B
bellard 已提交
328
static int fdctrl_transfer_handler (void *opaque, int nchan,
A
Anthony Liguori 已提交
329
                                    int dma_pos, int dma_len);
330
static void fdctrl_raise_irq(FDCtrl *fdctrl);
331
static FDrive *get_cur_drv(FDCtrl *fdctrl);
B
Blue Swirl 已提交
332 333 334 335 336 337 338 339 340 341 342 343

static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
static uint32_t fdctrl_read_dor(FDCtrl *fdctrl);
static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_tape(FDCtrl *fdctrl);
static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl);
static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
344
static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
B
bellard 已提交
345 346 347 348 349 350 351

enum {
    FD_DIR_WRITE   = 0,
    FD_DIR_READ    = 1,
    FD_DIR_SCANE   = 2,
    FD_DIR_SCANL   = 3,
    FD_DIR_SCANH   = 4,
H
Hervé Poussineau 已提交
352
    FD_DIR_VERIFY  = 5,
B
bellard 已提交
353 354 355
};

enum {
B
blueswir1 已提交
356 357
    FD_STATE_MULTI  = 0x01,	/* multi track flag */
    FD_STATE_FORMAT = 0x02,	/* format flag */
B
bellard 已提交
358 359
};

360
enum {
B
blueswir1 已提交
361 362
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
363 364 365 366 367 368
    FD_REG_DOR = 0x02,
    FD_REG_TDR = 0x03,
    FD_REG_MSR = 0x04,
    FD_REG_DSR = 0x04,
    FD_REG_FIFO = 0x05,
    FD_REG_DIR = 0x07,
369
    FD_REG_CCR = 0x07,
370 371 372
};

enum {
373
    FD_CMD_READ_TRACK = 0x02,
374 375
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
376 377
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
378 379
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
380 381 382 383
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
384 385 386
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
387
    FD_CMD_SCAN_EQUAL = 0x11,
388 389
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
390 391
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
392 393
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
394 395
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
396
    FD_CMD_SAVE = 0x2e,
397
    FD_CMD_OPTION = 0x33,
398
    FD_CMD_RESTORE = 0x4e,
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
    FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
    FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
    FD_CMD_FORMAT_AND_WRITE = 0xcd,
    FD_CMD_RELATIVE_SEEK_IN = 0xcf,
};

enum {
    FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */
    FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */
    FD_CONFIG_POLL  = 0x10, /* Poll enabled */
    FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */
    FD_CONFIG_EIS   = 0x40, /* No implied seeks */
};

enum {
P
Pavel Hrdina 已提交
414 415 416
    FD_SR0_DS0      = 0x01,
    FD_SR0_DS1      = 0x02,
    FD_SR0_HEAD     = 0x04,
417 418 419 420 421 422 423
    FD_SR0_EQPMT    = 0x10,
    FD_SR0_SEEK     = 0x20,
    FD_SR0_ABNTERM  = 0x40,
    FD_SR0_INVCMD   = 0x80,
    FD_SR0_RDYCHG   = 0xc0,
};

B
blueswir1 已提交
424
enum {
425
    FD_SR1_MA       = 0x01, /* Missing address mark */
426
    FD_SR1_NW       = 0x02, /* Not writable */
B
blueswir1 已提交
427 428 429 430 431 432 433 434
    FD_SR1_EC       = 0x80, /* End of cylinder */
};

enum {
    FD_SR2_SNS      = 0x04, /* Scan not satisfied */
    FD_SR2_SEH      = 0x08, /* Scan equal hit */
};

B
blueswir1 已提交
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
enum {
    FD_SRA_DIR      = 0x01,
    FD_SRA_nWP      = 0x02,
    FD_SRA_nINDX    = 0x04,
    FD_SRA_HDSEL    = 0x08,
    FD_SRA_nTRK0    = 0x10,
    FD_SRA_STEP     = 0x20,
    FD_SRA_nDRV2    = 0x40,
    FD_SRA_INTPEND  = 0x80,
};

enum {
    FD_SRB_MTR0     = 0x01,
    FD_SRB_MTR1     = 0x02,
    FD_SRB_WGATE    = 0x04,
    FD_SRB_RDATA    = 0x08,
    FD_SRB_WDATA    = 0x10,
    FD_SRB_DR0      = 0x20,
};

455
enum {
B
blueswir1 已提交
456 457 458
#if MAX_FD == 4
    FD_DOR_SELMASK  = 0x03,
#else
459
    FD_DOR_SELMASK  = 0x01,
B
blueswir1 已提交
460
#endif
461 462 463 464 465 466 467 468 469
    FD_DOR_nRESET   = 0x04,
    FD_DOR_DMAEN    = 0x08,
    FD_DOR_MOTEN0   = 0x10,
    FD_DOR_MOTEN1   = 0x20,
    FD_DOR_MOTEN2   = 0x40,
    FD_DOR_MOTEN3   = 0x80,
};

enum {
B
blueswir1 已提交
470
#if MAX_FD == 4
471
    FD_TDR_BOOTSEL  = 0x0c,
B
blueswir1 已提交
472 473 474
#else
    FD_TDR_BOOTSEL  = 0x04,
#endif
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
};

enum {
    FD_DSR_DRATEMASK= 0x03,
    FD_DSR_PWRDOWN  = 0x40,
    FD_DSR_SWRESET  = 0x80,
};

enum {
    FD_MSR_DRV0BUSY = 0x01,
    FD_MSR_DRV1BUSY = 0x02,
    FD_MSR_DRV2BUSY = 0x04,
    FD_MSR_DRV3BUSY = 0x08,
    FD_MSR_CMDBUSY  = 0x10,
    FD_MSR_NONDMA   = 0x20,
    FD_MSR_DIO      = 0x40,
    FD_MSR_RQM      = 0x80,
};

enum {
    FD_DIR_DSKCHG   = 0x80,
};

B
bellard 已提交
498
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
499
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
B
bellard 已提交
500

B
Blue Swirl 已提交
501
struct FDCtrl {
A
Avi Kivity 已提交
502
    MemoryRegion iomem;
P
pbrook 已提交
503
    qemu_irq irq;
B
bellard 已提交
504
    /* Controller state */
505
    QEMUTimer *result_timer;
506 507 508 509
    int dma_chann;
    /* Controller's identification */
    uint8_t version;
    /* HW */
B
blueswir1 已提交
510 511
    uint8_t sra;
    uint8_t srb;
B
blueswir1 已提交
512
    uint8_t dor;
J
Juan Quintela 已提交
513
    uint8_t dor_vmstate; /* only used as temp during vmstate */
B
blueswir1 已提交
514
    uint8_t tdr;
B
blueswir1 已提交
515
    uint8_t dsr;
B
blueswir1 已提交
516
    uint8_t msr;
B
bellard 已提交
517
    uint8_t cur_drv;
B
blueswir1 已提交
518 519 520
    uint8_t status0;
    uint8_t status1;
    uint8_t status2;
B
bellard 已提交
521
    /* Command FIFO */
522
    uint8_t *fifo;
J
Juan Quintela 已提交
523
    int32_t fifo_size;
B
bellard 已提交
524 525 526 527
    uint32_t data_pos;
    uint32_t data_len;
    uint8_t data_state;
    uint8_t data_dir;
528
    uint8_t eot; /* last wanted sector */
B
bellard 已提交
529 530 531 532 533 534 535 536
    /* States kept only to be returned back */
    /* 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 */
J
Juan Quintela 已提交
537
    uint8_t num_floppies;
538 539
    /* Sun4m quirks? */
    int sun4m;
B
Blue Swirl 已提交
540
    FDrive drives[MAX_FD];
541
    int reset_sensei;
542
    uint32_t check_media_rate;
543 544 545
    /* Timers state */
    uint8_t timer0;
    uint8_t timer1;
546 547
};

B
Blue Swirl 已提交
548
typedef struct FDCtrlSysBus {
G
Gerd Hoffmann 已提交
549
    SysBusDevice busdev;
B
Blue Swirl 已提交
550 551
    struct FDCtrl state;
} FDCtrlSysBus;
G
Gerd Hoffmann 已提交
552

B
Blue Swirl 已提交
553
typedef struct FDCtrlISABus {
G
Gerd Hoffmann 已提交
554
    ISADevice busdev;
555 556 557
    uint32_t iobase;
    uint32_t irq;
    uint32_t dma;
B
Blue Swirl 已提交
558
    struct FDCtrl state;
559 560
    int32_t bootindexA;
    int32_t bootindexB;
B
Blue Swirl 已提交
561
} FDCtrlISABus;
G
Gerd Hoffmann 已提交
562

563 564
static uint32_t fdctrl_read (void *opaque, uint32_t reg)
{
B
Blue Swirl 已提交
565
    FDCtrl *fdctrl = opaque;
566 567
    uint32_t retval;

K
Kevin Wolf 已提交
568
    reg &= 7;
B
blueswir1 已提交
569
    switch (reg) {
B
blueswir1 已提交
570 571
    case FD_REG_SRA:
        retval = fdctrl_read_statusA(fdctrl);
572
        break;
B
blueswir1 已提交
573
    case FD_REG_SRB:
574 575
        retval = fdctrl_read_statusB(fdctrl);
        break;
576
    case FD_REG_DOR:
577 578
        retval = fdctrl_read_dor(fdctrl);
        break;
579
    case FD_REG_TDR:
580
        retval = fdctrl_read_tape(fdctrl);
581
        break;
582
    case FD_REG_MSR:
583
        retval = fdctrl_read_main_status(fdctrl);
584
        break;
585
    case FD_REG_FIFO:
586
        retval = fdctrl_read_data(fdctrl);
587
        break;
588
    case FD_REG_DIR:
589
        retval = fdctrl_read_dir(fdctrl);
590
        break;
591
    default:
592 593
        retval = (uint32_t)(-1);
        break;
594
    }
595
    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
596 597 598 599 600 601

    return retval;
}

static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
B
Blue Swirl 已提交
602
    FDCtrl *fdctrl = opaque;
603

604 605
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);

K
Kevin Wolf 已提交
606
    reg &= 7;
B
blueswir1 已提交
607
    switch (reg) {
608
    case FD_REG_DOR:
609 610
        fdctrl_write_dor(fdctrl, value);
        break;
611
    case FD_REG_TDR:
612
        fdctrl_write_tape(fdctrl, value);
613
        break;
614
    case FD_REG_DSR:
615
        fdctrl_write_rate(fdctrl, value);
616
        break;
617
    case FD_REG_FIFO:
618
        fdctrl_write_data(fdctrl, value);
619
        break;
620 621 622
    case FD_REG_CCR:
        fdctrl_write_ccr(fdctrl, value);
        break;
623
    default:
624
        break;
625
    }
626 627
}

A
Avi Kivity 已提交
628
static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg,
A
Avi Kivity 已提交
629
                                 unsigned ize)
B
bellard 已提交
630
{
631
    return fdctrl_read(opaque, (uint32_t)reg);
B
bellard 已提交
632 633
}

A
Avi Kivity 已提交
634
static void fdctrl_write_mem (void *opaque, hwaddr reg,
A
Avi Kivity 已提交
635
                              uint64_t value, unsigned size)
B
bellard 已提交
636
{
637
    fdctrl_write(opaque, (uint32_t)reg, value);
B
bellard 已提交
638 639
}

A
Avi Kivity 已提交
640 641 642 643
static const MemoryRegionOps fdctrl_mem_ops = {
    .read = fdctrl_read_mem,
    .write = fdctrl_write_mem,
    .endianness = DEVICE_NATIVE_ENDIAN,
B
bellard 已提交
644 645
};

A
Avi Kivity 已提交
646 647 648 649 650 651 652 653
static const MemoryRegionOps fdctrl_mem_strict_ops = {
    .read = fdctrl_read_mem,
    .write = fdctrl_write_mem,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 1,
        .max_access_size = 1,
    },
654 655
};

656 657 658 659
static bool fdrive_media_changed_needed(void *opaque)
{
    FDrive *drive = opaque;

660
    return (drive->bs != NULL && drive->media_changed != 1);
661 662 663 664 665 666 667 668 669 670 671 672 673
}

static const VMStateDescription vmstate_fdrive_media_changed = {
    .name = "fdrive/media_changed",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields      = (VMStateField[]) {
        VMSTATE_UINT8(media_changed, FDrive),
        VMSTATE_END_OF_LIST()
    }
};

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
static bool fdrive_media_rate_needed(void *opaque)
{
    FDrive *drive = opaque;

    return drive->fdctrl->check_media_rate;
}

static const VMStateDescription vmstate_fdrive_media_rate = {
    .name = "fdrive/media_rate",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields      = (VMStateField[]) {
        VMSTATE_UINT8(media_rate, FDrive),
        VMSTATE_END_OF_LIST()
    }
};

J
Juan Quintela 已提交
692 693 694 695 696
static const VMStateDescription vmstate_fdrive = {
    .name = "fdrive",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
697
    .fields      = (VMStateField[]) {
B
Blue Swirl 已提交
698 699 700
        VMSTATE_UINT8(head, FDrive),
        VMSTATE_UINT8(track, FDrive),
        VMSTATE_UINT8(sect, FDrive),
J
Juan Quintela 已提交
701
        VMSTATE_END_OF_LIST()
702 703 704 705 706
    },
    .subsections = (VMStateSubsection[]) {
        {
            .vmsd = &vmstate_fdrive_media_changed,
            .needed = &fdrive_media_changed_needed,
707 708 709
        } , {
            .vmsd = &vmstate_fdrive_media_rate,
            .needed = &fdrive_media_rate_needed,
710 711 712
        } , {
            /* empty */
        }
J
Juan Quintela 已提交
713 714
    }
};
715

716
static void fdc_pre_save(void *opaque)
717
{
B
Blue Swirl 已提交
718
    FDCtrl *s = opaque;
719

J
Juan Quintela 已提交
720
    s->dor_vmstate = s->dor | GET_CUR_DRV(s);
721 722
}

723
static int fdc_post_load(void *opaque, int version_id)
724
{
B
Blue Swirl 已提交
725
    FDCtrl *s = opaque;
726

J
Juan Quintela 已提交
727 728
    SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
    s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
729 730 731
    return 0;
}

J
Juan Quintela 已提交
732
static const VMStateDescription vmstate_fdc = {
733
    .name = "fdc",
J
Juan Quintela 已提交
734 735 736 737 738 739 740
    .version_id = 2,
    .minimum_version_id = 2,
    .minimum_version_id_old = 2,
    .pre_save = fdc_pre_save,
    .post_load = fdc_post_load,
    .fields      = (VMStateField []) {
        /* Controller State */
B
Blue Swirl 已提交
741 742 743 744 745 746 747 748 749
        VMSTATE_UINT8(sra, FDCtrl),
        VMSTATE_UINT8(srb, FDCtrl),
        VMSTATE_UINT8(dor_vmstate, FDCtrl),
        VMSTATE_UINT8(tdr, FDCtrl),
        VMSTATE_UINT8(dsr, FDCtrl),
        VMSTATE_UINT8(msr, FDCtrl),
        VMSTATE_UINT8(status0, FDCtrl),
        VMSTATE_UINT8(status1, FDCtrl),
        VMSTATE_UINT8(status2, FDCtrl),
J
Juan Quintela 已提交
750
        /* Command FIFO */
B
Blue Swirl 已提交
751 752
        VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8,
                             uint8_t),
B
Blue Swirl 已提交
753 754 755 756 757
        VMSTATE_UINT32(data_pos, FDCtrl),
        VMSTATE_UINT32(data_len, FDCtrl),
        VMSTATE_UINT8(data_state, FDCtrl),
        VMSTATE_UINT8(data_dir, FDCtrl),
        VMSTATE_UINT8(eot, FDCtrl),
J
Juan Quintela 已提交
758
        /* States kept only to be returned back */
B
Blue Swirl 已提交
759 760 761 762 763 764 765 766 767
        VMSTATE_UINT8(timer0, FDCtrl),
        VMSTATE_UINT8(timer1, FDCtrl),
        VMSTATE_UINT8(precomp_trk, FDCtrl),
        VMSTATE_UINT8(config, FDCtrl),
        VMSTATE_UINT8(lock, FDCtrl),
        VMSTATE_UINT8(pwrd, FDCtrl),
        VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl),
        VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1,
                             vmstate_fdrive, FDrive),
J
Juan Quintela 已提交
768
        VMSTATE_END_OF_LIST()
B
blueswir1 已提交
769
    }
J
Juan Quintela 已提交
770
};
771

B
Blue Swirl 已提交
772
static void fdctrl_external_reset_sysbus(DeviceState *d)
773
{
B
Blue Swirl 已提交
774 775
    FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev);
    FDCtrl *s = &sys->state;
B
Blue Swirl 已提交
776 777 778 779 780 781

    fdctrl_reset(s, 0);
}

static void fdctrl_external_reset_isa(DeviceState *d)
{
B
Blue Swirl 已提交
782 783
    FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev);
    FDCtrl *s = &isa->state;
784 785 786 787

    fdctrl_reset(s, 0);
}

B
blueswir1 已提交
788 789
static void fdctrl_handle_tc(void *opaque, int irq, int level)
{
B
Blue Swirl 已提交
790
    //FDCtrl *s = opaque;
B
blueswir1 已提交
791 792 793 794 795 796 797

    if (level) {
        // XXX
        FLOPPY_DPRINTF("TC pulsed\n");
    }
}

B
bellard 已提交
798
/* Change IRQ state */
B
Blue Swirl 已提交
799
static void fdctrl_reset_irq(FDCtrl *fdctrl)
B
bellard 已提交
800
{
801
    fdctrl->status0 = 0;
B
blueswir1 已提交
802 803
    if (!(fdctrl->sra & FD_SRA_INTPEND))
        return;
804
    FLOPPY_DPRINTF("Reset interrupt\n");
P
pbrook 已提交
805
    qemu_set_irq(fdctrl->irq, 0);
B
blueswir1 已提交
806
    fdctrl->sra &= ~FD_SRA_INTPEND;
B
bellard 已提交
807 808
}

809
static void fdctrl_raise_irq(FDCtrl *fdctrl)
B
bellard 已提交
810
{
B
blueswir1 已提交
811 812 813 814 815
    /* Sparc mutation */
    if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
        /* XXX: not sure */
        fdctrl->msr &= ~FD_MSR_CMDBUSY;
        fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
816
        return;
B
bellard 已提交
817
    }
B
blueswir1 已提交
818
    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
P
pbrook 已提交
819
        qemu_set_irq(fdctrl->irq, 1);
B
blueswir1 已提交
820
        fdctrl->sra |= FD_SRA_INTPEND;
B
bellard 已提交
821
    }
822

823
    fdctrl->reset_sensei = 0;
B
blueswir1 已提交
824
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
B
bellard 已提交
825 826
}

B
bellard 已提交
827
/* Reset controller */
B
Blue Swirl 已提交
828
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
B
bellard 已提交
829 830 831
{
    int i;

B
bellard 已提交
832
    FLOPPY_DPRINTF("reset controller\n");
833
    fdctrl_reset_irq(fdctrl);
B
bellard 已提交
834
    /* Initialise controller */
B
blueswir1 已提交
835 836 837 838
    fdctrl->sra = 0;
    fdctrl->srb = 0xc0;
    if (!fdctrl->drives[1].bs)
        fdctrl->sra |= FD_SRA_nDRV2;
839
    fdctrl->cur_drv = 0;
B
blueswir1 已提交
840
    fdctrl->dor = FD_DOR_nRESET;
B
blueswir1 已提交
841
    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
B
blueswir1 已提交
842
    fdctrl->msr = FD_MSR_RQM;
B
bellard 已提交
843
    /* FIFO state */
844 845
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
B
blueswir1 已提交
846
    fdctrl->data_state = 0;
847
    fdctrl->data_dir = FD_DIR_WRITE;
B
bellard 已提交
848
    for (i = 0; i < MAX_FD; i++)
B
blueswir1 已提交
849
        fd_recalibrate(&fdctrl->drives[i]);
850
    fdctrl_reset_fifo(fdctrl);
B
blueswir1 已提交
851
    if (do_irq) {
852 853
        fdctrl->status0 |= FD_SR0_RDYCHG;
        fdctrl_raise_irq(fdctrl);
854
        fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
B
blueswir1 已提交
855
    }
856 857
}

B
Blue Swirl 已提交
858
static inline FDrive *drv0(FDCtrl *fdctrl)
859
{
B
blueswir1 已提交
860
    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
861 862
}

B
Blue Swirl 已提交
863
static inline FDrive *drv1(FDCtrl *fdctrl)
864
{
B
blueswir1 已提交
865 866 867 868
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
        return &fdctrl->drives[1];
    else
        return &fdctrl->drives[0];
869 870
}

B
blueswir1 已提交
871
#if MAX_FD == 4
B
Blue Swirl 已提交
872
static inline FDrive *drv2(FDCtrl *fdctrl)
B
blueswir1 已提交
873 874 875 876 877 878 879
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
        return &fdctrl->drives[2];
    else
        return &fdctrl->drives[1];
}

B
Blue Swirl 已提交
880
static inline FDrive *drv3(FDCtrl *fdctrl)
B
blueswir1 已提交
881 882 883 884 885 886 887 888
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
        return &fdctrl->drives[3];
    else
        return &fdctrl->drives[2];
}
#endif

B
Blue Swirl 已提交
889
static FDrive *get_cur_drv(FDCtrl *fdctrl)
890
{
B
blueswir1 已提交
891 892 893 894 895 896 897 898 899
    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;
    }
B
bellard 已提交
900 901
}

B
blueswir1 已提交
902
/* Status A register : 0x00 (read-only) */
B
Blue Swirl 已提交
903
static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
B
blueswir1 已提交
904 905 906 907 908 909 910 911
{
    uint32_t retval = fdctrl->sra;

    FLOPPY_DPRINTF("status register A: 0x%02x\n", retval);

    return retval;
}

B
bellard 已提交
912
/* Status B register : 0x01 (read-only) */
B
Blue Swirl 已提交
913
static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl)
B
bellard 已提交
914
{
B
blueswir1 已提交
915 916 917 918 919
    uint32_t retval = fdctrl->srb;

    FLOPPY_DPRINTF("status register B: 0x%02x\n", retval);

    return retval;
B
bellard 已提交
920 921 922
}

/* Digital output register : 0x02 */
B
Blue Swirl 已提交
923
static uint32_t fdctrl_read_dor(FDCtrl *fdctrl)
B
bellard 已提交
924
{
B
blueswir1 已提交
925
    uint32_t retval = fdctrl->dor;
B
bellard 已提交
926 927

    /* Selected drive */
928
    retval |= fdctrl->cur_drv;
B
bellard 已提交
929 930 931 932 933
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}

B
Blue Swirl 已提交
934
static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
935 936
{
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
B
blueswir1 已提交
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953

    /* Motors */
    if (value & FD_DOR_MOTEN0)
        fdctrl->srb |= FD_SRB_MTR0;
    else
        fdctrl->srb &= ~FD_SRB_MTR0;
    if (value & FD_DOR_MOTEN1)
        fdctrl->srb |= FD_SRB_MTR1;
    else
        fdctrl->srb &= ~FD_SRB_MTR1;

    /* Drive */
    if (value & 1)
        fdctrl->srb |= FD_SRB_DR0;
    else
        fdctrl->srb &= ~FD_SRB_DR0;

B
bellard 已提交
954
    /* Reset */
955
    if (!(value & FD_DOR_nRESET)) {
B
blueswir1 已提交
956
        if (fdctrl->dor & FD_DOR_nRESET) {
B
bellard 已提交
957
            FLOPPY_DPRINTF("controller enter RESET state\n");
B
bellard 已提交
958 959
        }
    } else {
B
blueswir1 已提交
960
        if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
961
            FLOPPY_DPRINTF("controller out of RESET state\n");
962
            fdctrl_reset(fdctrl, 1);
B
blueswir1 已提交
963
            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
bellard 已提交
964 965 966
        }
    }
    /* Selected drive */
967
    fdctrl->cur_drv = value & FD_DOR_SELMASK;
B
blueswir1 已提交
968 969

    fdctrl->dor = value;
B
bellard 已提交
970 971 972
}

/* Tape drive register : 0x03 */
B
Blue Swirl 已提交
973
static uint32_t fdctrl_read_tape(FDCtrl *fdctrl)
B
bellard 已提交
974
{
B
blueswir1 已提交
975
    uint32_t retval = fdctrl->tdr;
B
bellard 已提交
976 977 978 979 980 981

    FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);

    return retval;
}

B
Blue Swirl 已提交
982
static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
983 984
{
    /* Reset mode */
B
blueswir1 已提交
985
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
986
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
987 988 989 990
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
B
blueswir1 已提交
991
    fdctrl->tdr = value & FD_TDR_BOOTSEL;
B
bellard 已提交
992 993 994 995
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
B
Blue Swirl 已提交
996
static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
B
bellard 已提交
997
{
B
blueswir1 已提交
998
    uint32_t retval = fdctrl->msr;
B
bellard 已提交
999

B
blueswir1 已提交
1000
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
blueswir1 已提交
1001
    fdctrl->dor |= FD_DOR_nRESET;
B
blueswir1 已提交
1002

1003 1004 1005 1006 1007 1008
    /* Sparc mutation */
    if (fdctrl->sun4m) {
        retval |= FD_MSR_DIO;
        fdctrl_reset_irq(fdctrl);
    };

B
bellard 已提交
1009 1010 1011 1012 1013 1014
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
B
Blue Swirl 已提交
1015
static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
1016 1017
{
    /* Reset mode */
B
blueswir1 已提交
1018
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
1019 1020 1021
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
B
bellard 已提交
1022 1023
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
1024
    if (value & FD_DSR_SWRESET) {
B
blueswir1 已提交
1025
        fdctrl->dor &= ~FD_DOR_nRESET;
1026
        fdctrl_reset(fdctrl, 1);
B
blueswir1 已提交
1027
        fdctrl->dor |= FD_DOR_nRESET;
B
bellard 已提交
1028
    }
1029
    if (value & FD_DSR_PWRDOWN) {
1030
        fdctrl_reset(fdctrl, 1);
B
bellard 已提交
1031
    }
B
blueswir1 已提交
1032
    fdctrl->dsr = value;
B
bellard 已提交
1033 1034
}

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
/* Configuration control register: 0x07 (write) */
static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value)
{
    /* Reset mode */
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
    FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);

    /* Only the rate selection bits used in AT mode, and we
     * store those in the DSR.
     */
    fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) |
                  (value & FD_DSR_DRATEMASK);
}

B
Blue Swirl 已提交
1052
static int fdctrl_media_changed(FDrive *drv)
B
bellard 已提交
1053
{
1054
    return drv->media_changed;
B
bellard 已提交
1055 1056
}

B
bellard 已提交
1057
/* Digital input register : 0x07 (read-only) */
B
Blue Swirl 已提交
1058
static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
B
bellard 已提交
1059 1060 1061
{
    uint32_t retval = 0;

1062
    if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
1063
        retval |= FD_DIR_DSKCHG;
1064
    }
1065
    if (retval != 0) {
1066
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
1067
    }
B
bellard 已提交
1068 1069 1070 1071 1072

    return retval;
}

/* FIFO state control */
B
Blue Swirl 已提交
1073
static void fdctrl_reset_fifo(FDCtrl *fdctrl)
B
bellard 已提交
1074
{
1075 1076
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
B
blueswir1 已提交
1077
    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
B
bellard 已提交
1078 1079 1080
}

/* Set FIFO status for the host to read */
1081
static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len)
B
bellard 已提交
1082
{
1083 1084 1085
    fdctrl->data_dir = FD_DIR_READ;
    fdctrl->data_len = fifo_len;
    fdctrl->data_pos = 0;
B
blueswir1 已提交
1086
    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
B
bellard 已提交
1087 1088 1089
}

/* Set an error: unimplemented/unknown command */
B
Blue Swirl 已提交
1090
static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
B
bellard 已提交
1091
{
B
Blue Swirl 已提交
1092 1093
    qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
                  fdctrl->fifo[0]);
1094
    fdctrl->fifo[0] = FD_SR0_INVCMD;
1095
    fdctrl_set_fifo(fdctrl, 1);
B
bellard 已提交
1096 1097
}

1098 1099 1100 1101
/* Seek to next sector
 * returns 0 when end of track reached (for DBL_SIDES on head 1)
 * otherwise returns 1
 */
B
Blue Swirl 已提交
1102
static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
B
blueswir1 已提交
1103 1104 1105 1106 1107 1108
{
    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
                   cur_drv->head, cur_drv->track, cur_drv->sect,
                   fd_sector(cur_drv));
    /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
       error in fact */
1109 1110 1111 1112 1113 1114 1115 1116 1117
    uint8_t new_head = cur_drv->head;
    uint8_t new_track = cur_drv->track;
    uint8_t new_sect = cur_drv->sect;

    int ret = 1;

    if (new_sect >= cur_drv->last_sect ||
        new_sect == fdctrl->eot) {
        new_sect = 1;
B
blueswir1 已提交
1118
        if (FD_MULTI_TRACK(fdctrl->data_state)) {
1119
            if (new_head == 0 &&
B
blueswir1 已提交
1120
                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1121
                new_head = 1;
B
blueswir1 已提交
1122
            } else {
1123 1124
                new_head = 0;
                new_track++;
1125
                fdctrl->status0 |= FD_SR0_SEEK;
1126 1127 1128
                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
                    ret = 0;
                }
B
blueswir1 已提交
1129 1130
            }
        } else {
1131
            fdctrl->status0 |= FD_SR0_SEEK;
1132 1133 1134 1135 1136 1137
            new_track++;
            ret = 0;
        }
        if (ret == 1) {
            FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
                    new_head, new_track, new_sect, fd_sector(cur_drv));
B
blueswir1 已提交
1138 1139
        }
    } else {
1140
        new_sect++;
B
blueswir1 已提交
1141
    }
1142 1143
    fd_seek(cur_drv, new_head, new_track, new_sect, 1);
    return ret;
B
blueswir1 已提交
1144 1145
}

B
bellard 已提交
1146
/* Callback for transfer end (stop or abort) */
B
Blue Swirl 已提交
1147 1148
static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
                                 uint8_t status1, uint8_t status2)
B
bellard 已提交
1149
{
B
Blue Swirl 已提交
1150
    FDrive *cur_drv;
1151
    cur_drv = get_cur_drv(fdctrl);
H
Hervé Poussineau 已提交
1152 1153 1154 1155 1156 1157 1158

    fdctrl->status0 &= ~(FD_SR0_DS0 | FD_SR0_DS1 | FD_SR0_HEAD);
    fdctrl->status0 |= GET_CUR_DRV(fdctrl);
    if (cur_drv->head) {
        fdctrl->status0 |= FD_SR0_HEAD;
    }
    fdctrl->status0 |= status0;
P
Pavel Hrdina 已提交
1159

B
bellard 已提交
1160
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
P
Pavel Hrdina 已提交
1161 1162
                   status0, status1, status2, fdctrl->status0);
    fdctrl->fifo[0] = fdctrl->status0;
1163 1164 1165 1166 1167 1168 1169
    fdctrl->fifo[1] = status1;
    fdctrl->fifo[2] = status2;
    fdctrl->fifo[3] = cur_drv->track;
    fdctrl->fifo[4] = cur_drv->head;
    fdctrl->fifo[5] = cur_drv->sect;
    fdctrl->fifo[6] = FD_SECTOR_SC;
    fdctrl->data_dir = FD_DIR_READ;
B
blueswir1 已提交
1170
    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1171
        DMA_release_DREQ(fdctrl->dma_chann);
1172
    }
B
blueswir1 已提交
1173
    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
B
blueswir1 已提交
1174
    fdctrl->msr &= ~FD_MSR_NONDMA;
1175 1176

    fdctrl_set_fifo(fdctrl, 7);
1177
    fdctrl_raise_irq(fdctrl);
B
bellard 已提交
1178 1179 1180
}

/* Prepare a data transfer (either DMA or FIFO) */
B
Blue Swirl 已提交
1181
static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
B
bellard 已提交
1182
{
B
Blue Swirl 已提交
1183
    FDrive *cur_drv;
B
bellard 已提交
1184 1185
    uint8_t kh, kt, ks;

B
blueswir1 已提交
1186
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1187 1188 1189 1190
    cur_drv = get_cur_drv(fdctrl);
    kt = fdctrl->fifo[2];
    kh = fdctrl->fifo[3];
    ks = fdctrl->fifo[4];
B
bellard 已提交
1191
    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
B
blueswir1 已提交
1192
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1193 1194
                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
                                  NUM_SIDES(cur_drv)));
B
blueswir1 已提交
1195
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
B
bellard 已提交
1196 1197
    case 2:
        /* sect too big */
1198
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1199 1200 1201
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1202 1203 1204
        return;
    case 3:
        /* track too big */
B
blueswir1 已提交
1205
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1206 1207 1208
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1209 1210 1211
        return;
    case 4:
        /* No seek enabled */
1212
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1213 1214 1215
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1216 1217
        return;
    case 1:
1218
        fdctrl->status0 |= FD_SR0_SEEK;
B
bellard 已提交
1219 1220 1221 1222
        break;
    default:
        break;
    }
B
blueswir1 已提交
1223

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
    /* Check the data rate. If the programmed data rate does not match
     * the currently inserted medium, the operation has to fail. */
    if (fdctrl->check_media_rate &&
        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
        FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    }

B
bellard 已提交
1237
    /* Set the FIFO state */
1238 1239
    fdctrl->data_dir = direction;
    fdctrl->data_pos = 0;
1240
    assert(fdctrl->msr & FD_MSR_CMDBUSY);
1241 1242 1243 1244
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
H
Hervé Poussineau 已提交
1245
    if (fdctrl->fifo[5] == 0) {
1246 1247
        fdctrl->data_len = fdctrl->fifo[8];
    } else {
1248
        int tmp;
T
ths 已提交
1249
        fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
1250
        tmp = (fdctrl->fifo[6] - ks + 1);
1251
        if (fdctrl->fifo[0] & 0x80)
1252
            tmp += fdctrl->fifo[6];
1253
        fdctrl->data_len *= tmp;
1254
    }
1255
    fdctrl->eot = fdctrl->fifo[6];
B
blueswir1 已提交
1256
    if (fdctrl->dor & FD_DOR_DMAEN) {
B
bellard 已提交
1257 1258
        int dma_mode;
        /* DMA transfer are enabled. Check if DMA channel is well programmed */
1259
        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
B
bellard 已提交
1260
        dma_mode = (dma_mode >> 2) & 3;
1261
        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
1262
                       dma_mode, direction,
1263
                       (128 << fdctrl->fifo[5]) *
1264
                       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
B
bellard 已提交
1265 1266 1267
        if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
              direction == FD_DIR_SCANH) && dma_mode == 0) ||
            (direction == FD_DIR_WRITE && dma_mode == 2) ||
H
Hervé Poussineau 已提交
1268 1269
            (direction == FD_DIR_READ && dma_mode == 1) ||
            (direction == FD_DIR_VERIFY)) {
B
bellard 已提交
1270
            /* No access is allowed until DMA transfer has completed */
B
blueswir1 已提交
1271
            fdctrl->msr &= ~FD_MSR_RQM;
H
Hervé Poussineau 已提交
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
            if (direction != FD_DIR_VERIFY) {
                /* Now, we just have to wait for the DMA controller to
                 * recall us...
                 */
                DMA_hold_DREQ(fdctrl->dma_chann);
                DMA_schedule(fdctrl->dma_chann);
            } else {
                /* Start transfer */
                fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
                                        fdctrl->data_len);
            }
B
bellard 已提交
1283
            return;
1284
        } else {
B
Blue Swirl 已提交
1285 1286
            FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode,
                           direction);
B
bellard 已提交
1287 1288 1289
        }
    }
    FLOPPY_DPRINTF("start non-DMA transfer\n");
B
blueswir1 已提交
1290
    fdctrl->msr |= FD_MSR_NONDMA;
B
blueswir1 已提交
1291 1292
    if (direction != FD_DIR_WRITE)
        fdctrl->msr |= FD_MSR_DIO;
B
bellard 已提交
1293
    /* IO based transfer: calculate len */
1294
    fdctrl_raise_irq(fdctrl);
B
bellard 已提交
1295 1296 1297
}

/* Prepare a transfer of deleted data */
B
Blue Swirl 已提交
1298
static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction)
B
bellard 已提交
1299
{
B
Blue Swirl 已提交
1300
    qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n");
B
blueswir1 已提交
1301

B
bellard 已提交
1302 1303 1304
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
1305
    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
B
bellard 已提交
1306 1307 1308
}

/* handlers for DMA transfers */
B
bellard 已提交
1309 1310
static int fdctrl_transfer_handler (void *opaque, int nchan,
                                    int dma_pos, int dma_len)
B
bellard 已提交
1311
{
B
Blue Swirl 已提交
1312 1313
    FDCtrl *fdctrl;
    FDrive *cur_drv;
1314
    int len, start_pos, rel_pos;
B
bellard 已提交
1315 1316
    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;

1317
    fdctrl = opaque;
B
blueswir1 已提交
1318
    if (fdctrl->msr & FD_MSR_RQM) {
B
bellard 已提交
1319 1320 1321
        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
        return 0;
    }
1322 1323 1324
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
        fdctrl->data_dir == FD_DIR_SCANH)
B
blueswir1 已提交
1325
        status2 = FD_SR2_SNS;
B
bellard 已提交
1326 1327
    if (dma_len > fdctrl->data_len)
        dma_len = fdctrl->data_len;
1328
    if (cur_drv->bs == NULL) {
1329
        if (fdctrl->data_dir == FD_DIR_WRITE)
1330
            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1331
        else
1332
            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1333
        len = 0;
1334 1335
        goto transfer_error;
    }
1336
    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
B
bellard 已提交
1337 1338
    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
        len = dma_len - fdctrl->data_pos;
1339 1340
        if (len + rel_pos > FD_SECTOR_LEN)
            len = FD_SECTOR_LEN - rel_pos;
B
bellard 已提交
1341 1342
        FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
                       "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
B
blueswir1 已提交
1343
                       fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
1344
                       cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
1345
                       fd_sector(cur_drv) * FD_SECTOR_LEN);
1346
        if (fdctrl->data_dir != FD_DIR_WRITE ||
1347
            len < FD_SECTOR_LEN || rel_pos != 0) {
1348 1349
            /* READ & SCAN commands and realign to a sector for WRITE */
            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1350
                          fdctrl->fifo, 1) < 0) {
B
bellard 已提交
1351 1352 1353
                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
                               fd_sector(cur_drv));
                /* Sure, image size is too small... */
1354
                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
B
bellard 已提交
1355
            }
1356
        }
1357 1358 1359
        switch (fdctrl->data_dir) {
        case FD_DIR_READ:
            /* READ commands */
B
bellard 已提交
1360 1361
            DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
                              fdctrl->data_pos, len);
1362 1363
            break;
        case FD_DIR_WRITE:
1364
            /* WRITE commands */
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374
            if (cur_drv->ro) {
                /* Handle readonly medium early, no need to do DMA, touch the
                 * LED or attempt any writes. A real floppy doesn't attempt
                 * to write to readonly media either. */
                fdctrl_stop_transfer(fdctrl,
                                     FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
                                     0x00);
                goto transfer_error;
            }

B
bellard 已提交
1375 1376
            DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
                             fdctrl->data_pos, len);
1377
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1378
                           fdctrl->fifo, 1) < 0) {
B
Blue Swirl 已提交
1379 1380
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
1381
                fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1382
                goto transfer_error;
1383
            }
1384
            break;
H
Hervé Poussineau 已提交
1385 1386 1387
        case FD_DIR_VERIFY:
            /* VERIFY commands */
            break;
1388 1389
        default:
            /* SCAN commands */
1390
            {
1391
                uint8_t tmpbuf[FD_SECTOR_LEN];
1392
                int ret;
B
bellard 已提交
1393
                DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
1394
                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
B
bellard 已提交
1395
                if (ret == 0) {
B
blueswir1 已提交
1396
                    status2 = FD_SR2_SEH;
B
bellard 已提交
1397 1398
                    goto end_transfer;
                }
1399 1400
                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
B
bellard 已提交
1401 1402 1403 1404
                    status2 = 0x00;
                    goto end_transfer;
                }
            }
1405
            break;
B
bellard 已提交
1406
        }
1407 1408
        fdctrl->data_pos += len;
        rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1409
        if (rel_pos == 0) {
B
bellard 已提交
1410
            /* Seek to next sector */
B
blueswir1 已提交
1411 1412
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
                break;
B
bellard 已提交
1413 1414
        }
    }
1415
 end_transfer:
1416 1417
    len = fdctrl->data_pos - start_pos;
    FLOPPY_DPRINTF("end transfer %d %d %d\n",
1418
                   fdctrl->data_pos, len, fdctrl->data_len);
1419 1420 1421
    if (fdctrl->data_dir == FD_DIR_SCANE ||
        fdctrl->data_dir == FD_DIR_SCANL ||
        fdctrl->data_dir == FD_DIR_SCANH)
B
blueswir1 已提交
1422
        status2 = FD_SR2_SEH;
1423
    fdctrl->data_len -= len;
1424
    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1425
 transfer_error:
B
bellard 已提交
1426

1427
    return len;
B
bellard 已提交
1428 1429 1430
}

/* Data register : 0x05 */
B
Blue Swirl 已提交
1431
static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
B
bellard 已提交
1432
{
B
Blue Swirl 已提交
1433
    FDrive *cur_drv;
B
bellard 已提交
1434
    uint32_t retval = 0;
B
blueswir1 已提交
1435
    int pos;
B
bellard 已提交
1436

1437
    cur_drv = get_cur_drv(fdctrl);
B
blueswir1 已提交
1438 1439
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
    if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
B
Blue Swirl 已提交
1440
        FLOPPY_DPRINTF("error: controller not ready for reading\n");
B
bellard 已提交
1441 1442
        return 0;
    }
1443
    pos = fdctrl->data_pos;
B
blueswir1 已提交
1444
    if (fdctrl->msr & FD_MSR_NONDMA) {
B
bellard 已提交
1445 1446
        pos %= FD_SECTOR_LEN;
        if (pos == 0) {
B
blueswir1 已提交
1447 1448 1449 1450 1451 1452
            if (fdctrl->data_pos != 0)
                if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                    FLOPPY_DPRINTF("error seeking to next sector %d\n",
                                   fd_sector(cur_drv));
                    return 0;
                }
B
blueswir1 已提交
1453 1454 1455 1456 1457 1458
            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 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);
            }
B
bellard 已提交
1459 1460
        }
    }
1461 1462 1463
    retval = fdctrl->fifo[pos];
    if (++fdctrl->data_pos == fdctrl->data_len) {
        fdctrl->data_pos = 0;
1464
        /* Switch from transfer mode to status mode
B
bellard 已提交
1465 1466
         * then from status mode to command mode
         */
B
blueswir1 已提交
1467
        if (fdctrl->msr & FD_MSR_NONDMA) {
1468
            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1469
        } else {
1470
            fdctrl_reset_fifo(fdctrl);
1471 1472
            fdctrl_reset_irq(fdctrl);
        }
B
bellard 已提交
1473 1474 1475 1476 1477 1478
    }
    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);

    return retval;
}

B
Blue Swirl 已提交
1479
static void fdctrl_format_sector(FDCtrl *fdctrl)
B
bellard 已提交
1480
{
B
Blue Swirl 已提交
1481
    FDrive *cur_drv;
1482
    uint8_t kh, kt, ks;
B
bellard 已提交
1483

B
blueswir1 已提交
1484
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1485 1486 1487 1488 1489
    cur_drv = get_cur_drv(fdctrl);
    kt = fdctrl->fifo[6];
    kh = fdctrl->fifo[7];
    ks = fdctrl->fifo[8];
    FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
B
blueswir1 已提交
1490
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1491 1492
                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
                                  NUM_SIDES(cur_drv)));
1493
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1494 1495
    case 2:
        /* sect too big */
1496
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1497 1498 1499 1500 1501 1502
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
B
blueswir1 已提交
1503
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1504 1505 1506 1507 1508 1509
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
1510
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1511 1512 1513 1514 1515
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 1:
1516
        fdctrl->status0 |= FD_SR0_SEEK;
1517 1518 1519 1520 1521 1522 1523
        break;
    default:
        break;
    }
    memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
    if (cur_drv->bs == NULL ||
        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
B
Blue Swirl 已提交
1524
        FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv));
1525
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1526
    } else {
1527 1528 1529
        if (cur_drv->sect == cur_drv->last_sect) {
            fdctrl->data_state &= ~FD_STATE_FORMAT;
            /* Last sector done */
1530
            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1531 1532 1533 1534 1535
        } else {
            /* More to do */
            fdctrl->data_pos = 0;
            fdctrl->data_len = 4;
        }
1536 1537 1538
    }
}

B
Blue Swirl 已提交
1539
static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
1540 1541 1542
{
    fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
    fdctrl->fifo[0] = fdctrl->lock << 4;
1543
    fdctrl_set_fifo(fdctrl, 1);
1544 1545
}

B
Blue Swirl 已提交
1546
static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
1547
{
B
Blue Swirl 已提交
1548
    FDrive *cur_drv = get_cur_drv(fdctrl);
1549 1550 1551 1552

    /* Drives position */
    fdctrl->fifo[0] = drv0(fdctrl)->track;
    fdctrl->fifo[1] = drv1(fdctrl)->track;
B
blueswir1 已提交
1553 1554 1555 1556
#if MAX_FD == 4
    fdctrl->fifo[2] = drv2(fdctrl)->track;
    fdctrl->fifo[3] = drv3(fdctrl)->track;
#else
1557 1558
    fdctrl->fifo[2] = 0;
    fdctrl->fifo[3] = 0;
B
blueswir1 已提交
1559
#endif
1560 1561
    /* timers */
    fdctrl->fifo[4] = fdctrl->timer0;
B
blueswir1 已提交
1562
    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1563 1564 1565 1566 1567
    fdctrl->fifo[6] = cur_drv->last_sect;
    fdctrl->fifo[7] = (fdctrl->lock << 7) |
        (cur_drv->perpendicular << 2);
    fdctrl->fifo[8] = fdctrl->config;
    fdctrl->fifo[9] = fdctrl->precomp_trk;
1568
    fdctrl_set_fifo(fdctrl, 10);
1569 1570
}

B
Blue Swirl 已提交
1571
static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
1572 1573 1574
{
    /* Controller's version */
    fdctrl->fifo[0] = fdctrl->version;
1575
    fdctrl_set_fifo(fdctrl, 1);
1576 1577
}

B
Blue Swirl 已提交
1578
static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
1579 1580
{
    fdctrl->fifo[0] = 0x41; /* Stepping 1 */
1581
    fdctrl_set_fifo(fdctrl, 1);
1582 1583
}

B
Blue Swirl 已提交
1584
static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
1585
{
B
Blue Swirl 已提交
1586
    FDrive *cur_drv = get_cur_drv(fdctrl);
1587 1588 1589 1590

    /* Drives position */
    drv0(fdctrl)->track = fdctrl->fifo[3];
    drv1(fdctrl)->track = fdctrl->fifo[4];
B
blueswir1 已提交
1591 1592 1593 1594
#if MAX_FD == 4
    drv2(fdctrl)->track = fdctrl->fifo[5];
    drv3(fdctrl)->track = fdctrl->fifo[6];
#endif
1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
    /* timers */
    fdctrl->timer0 = fdctrl->fifo[7];
    fdctrl->timer1 = fdctrl->fifo[8];
    cur_drv->last_sect = fdctrl->fifo[9];
    fdctrl->lock = fdctrl->fifo[10] >> 7;
    cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
    fdctrl->config = fdctrl->fifo[11];
    fdctrl->precomp_trk = fdctrl->fifo[12];
    fdctrl->pwrd = fdctrl->fifo[13];
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1607
static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
1608
{
B
Blue Swirl 已提交
1609
    FDrive *cur_drv = get_cur_drv(fdctrl);
1610 1611 1612 1613 1614 1615

    fdctrl->fifo[0] = 0;
    fdctrl->fifo[1] = 0;
    /* Drives position */
    fdctrl->fifo[2] = drv0(fdctrl)->track;
    fdctrl->fifo[3] = drv1(fdctrl)->track;
B
blueswir1 已提交
1616 1617 1618 1619
#if MAX_FD == 4
    fdctrl->fifo[4] = drv2(fdctrl)->track;
    fdctrl->fifo[5] = drv3(fdctrl)->track;
#else
1620 1621
    fdctrl->fifo[4] = 0;
    fdctrl->fifo[5] = 0;
B
blueswir1 已提交
1622
#endif
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
    /* timers */
    fdctrl->fifo[6] = fdctrl->timer0;
    fdctrl->fifo[7] = fdctrl->timer1;
    fdctrl->fifo[8] = cur_drv->last_sect;
    fdctrl->fifo[9] = (fdctrl->lock << 7) |
        (cur_drv->perpendicular << 2);
    fdctrl->fifo[10] = fdctrl->config;
    fdctrl->fifo[11] = fdctrl->precomp_trk;
    fdctrl->fifo[12] = fdctrl->pwrd;
    fdctrl->fifo[13] = 0;
    fdctrl->fifo[14] = 0;
1634
    fdctrl_set_fifo(fdctrl, 15);
1635 1636
}

B
Blue Swirl 已提交
1637
static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
1638
{
B
Blue Swirl 已提交
1639
    FDrive *cur_drv = get_cur_drv(fdctrl);
1640 1641 1642

    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
    qemu_mod_timer(fdctrl->result_timer,
1643
                   qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50));
1644 1645
}

B
Blue Swirl 已提交
1646
static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
1647
{
B
Blue Swirl 已提交
1648
    FDrive *cur_drv;
1649

B
blueswir1 已提交
1650
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
    cur_drv = get_cur_drv(fdctrl);
    fdctrl->data_state |= FD_STATE_FORMAT;
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
    cur_drv->bps =
        fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
#if 0
    cur_drv->last_sect =
        cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
        fdctrl->fifo[3] / 2;
#else
    cur_drv->last_sect = fdctrl->fifo[3];
#endif
    /* TODO: implement format using DMA expected by the Bochs BIOS
     * and Linux fdformat (read 3 bytes per sector via DMA and fill
     * the sector with the specified fill byte
     */
    fdctrl->data_state &= ~FD_STATE_FORMAT;
    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}

B
Blue Swirl 已提交
1674
static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
1675 1676 1677
{
    fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
    fdctrl->timer1 = fdctrl->fifo[2] >> 1;
B
blueswir1 已提交
1678 1679 1680 1681
    if (fdctrl->fifo[2] & 1)
        fdctrl->dor &= ~FD_DOR_DMAEN;
    else
        fdctrl->dor |= FD_DOR_DMAEN;
1682 1683 1684 1685
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1686
static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
1687
{
B
Blue Swirl 已提交
1688
    FDrive *cur_drv;
1689

B
blueswir1 已提交
1690
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1691 1692 1693 1694 1695 1696
    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) |
B
blueswir1 已提交
1697
        GET_CUR_DRV(fdctrl) |
1698
        0x28;
1699
    fdctrl_set_fifo(fdctrl, 1);
1700 1701
}

B
Blue Swirl 已提交
1702
static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
1703
{
B
Blue Swirl 已提交
1704
    FDrive *cur_drv;
1705

B
blueswir1 已提交
1706
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1707 1708 1709 1710
    cur_drv = get_cur_drv(fdctrl);
    fd_recalibrate(cur_drv);
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
1711 1712
    fdctrl->status0 |= FD_SR0_SEEK;
    fdctrl_raise_irq(fdctrl);
1713 1714
}

B
Blue Swirl 已提交
1715
static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
1716
{
B
Blue Swirl 已提交
1717
    FDrive *cur_drv = get_cur_drv(fdctrl);
1718

P
Pavel Hrdina 已提交
1719
    if (fdctrl->reset_sensei > 0) {
1720 1721 1722
        fdctrl->fifo[0] =
            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
        fdctrl->reset_sensei--;
P
Pavel Hrdina 已提交
1723 1724
    } else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
        fdctrl->fifo[0] = FD_SR0_INVCMD;
1725
        fdctrl_set_fifo(fdctrl, 1);
P
Pavel Hrdina 已提交
1726
        return;
1727 1728
    } else {
        fdctrl->fifo[0] =
P
Pavel Hrdina 已提交
1729 1730
                (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0))
                | GET_CUR_DRV(fdctrl);
1731 1732
    }

1733
    fdctrl->fifo[1] = cur_drv->track;
1734
    fdctrl_set_fifo(fdctrl, 2);
1735
    fdctrl_reset_irq(fdctrl);
B
blueswir1 已提交
1736
    fdctrl->status0 = FD_SR0_RDYCHG;
1737 1738
}

B
Blue Swirl 已提交
1739
static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
1740
{
B
Blue Swirl 已提交
1741
    FDrive *cur_drv;
1742

B
blueswir1 已提交
1743
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1744 1745
    cur_drv = get_cur_drv(fdctrl);
    fdctrl_reset_fifo(fdctrl);
1746 1747 1748
    /* The seek command just sends step pulses to the drive and doesn't care if
     * there is a medium inserted of if it's banging the head against the drive.
     */
1749
    fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
1750
    /* Raise Interrupt */
1751 1752
    fdctrl->status0 |= FD_SR0_SEEK;
    fdctrl_raise_irq(fdctrl);
1753 1754
}

B
Blue Swirl 已提交
1755
static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
1756
{
B
Blue Swirl 已提交
1757
    FDrive *cur_drv = get_cur_drv(fdctrl);
1758 1759 1760 1761

    if (fdctrl->fifo[1] & 0x80)
        cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
    /* No result back */
B
blueswir1 已提交
1762
    fdctrl_reset_fifo(fdctrl);
1763 1764
}

B
Blue Swirl 已提交
1765
static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
1766 1767 1768 1769 1770 1771 1772
{
    fdctrl->config = fdctrl->fifo[2];
    fdctrl->precomp_trk =  fdctrl->fifo[3];
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1773
static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
1774 1775 1776
{
    fdctrl->pwrd = fdctrl->fifo[1];
    fdctrl->fifo[0] = fdctrl->fifo[1];
1777
    fdctrl_set_fifo(fdctrl, 1);
1778 1779
}

B
Blue Swirl 已提交
1780
static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
1781 1782 1783 1784 1785
{
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1786
static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
1787
{
B
Blue Swirl 已提交
1788
    FDrive *cur_drv = get_cur_drv(fdctrl);
1789 1790 1791 1792 1793 1794 1795

    if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
        /* Command parameters done */
        if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
            fdctrl->fifo[0] = fdctrl->fifo[1];
            fdctrl->fifo[2] = 0;
            fdctrl->fifo[3] = 0;
1796
            fdctrl_set_fifo(fdctrl, 4);
1797 1798 1799 1800 1801 1802
        } else {
            fdctrl_reset_fifo(fdctrl);
        }
    } else if (fdctrl->data_len > 7) {
        /* ERROR */
        fdctrl->fifo[0] = 0x80 |
B
blueswir1 已提交
1803
            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1804
        fdctrl_set_fifo(fdctrl, 1);
1805 1806 1807
    }
}

P
Pavel Hrdina 已提交
1808
static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
1809
{
B
Blue Swirl 已提交
1810
    FDrive *cur_drv;
1811

B
blueswir1 已提交
1812
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1813 1814
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
1815 1816
        fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1,
                cur_drv->sect, 1);
1817
    } else {
P
Pavel Hrdina 已提交
1818 1819
        fd_seek(cur_drv, cur_drv->head,
                cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1);
1820 1821
    }
    fdctrl_reset_fifo(fdctrl);
B
blueswir1 已提交
1822
    /* Raise Interrupt */
1823 1824
    fdctrl->status0 |= FD_SR0_SEEK;
    fdctrl_raise_irq(fdctrl);
1825 1826
}

P
Pavel Hrdina 已提交
1827
static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
1828
{
B
Blue Swirl 已提交
1829
    FDrive *cur_drv;
1830

B
blueswir1 已提交
1831
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1832 1833
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->fifo[2] > cur_drv->track) {
1834
        fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1);
1835
    } else {
P
Pavel Hrdina 已提交
1836 1837
        fd_seek(cur_drv, cur_drv->head,
                cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1);
1838 1839 1840
    }
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
1841 1842
    fdctrl->status0 |= FD_SR0_SEEK;
    fdctrl_raise_irq(fdctrl);
1843 1844
}

B
blueswir1 已提交
1845 1846 1847 1848 1849
static const struct {
    uint8_t value;
    uint8_t mask;
    const char* name;
    int parameters;
B
Blue Swirl 已提交
1850
    void (*handler)(FDCtrl *fdctrl, int direction);
B
blueswir1 已提交
1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863
    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 },
H
Hervé Poussineau 已提交
1864
    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY },
B
blueswir1 已提交
1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888
    { 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 */
};
/* Associate command to an index in the 'handlers' array */
static uint8_t command_to_handler[256];

B
Blue Swirl 已提交
1889
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
1890
{
B
Blue Swirl 已提交
1891
    FDrive *cur_drv;
1892
    int pos;
1893

B
bellard 已提交
1894
    /* Reset mode */
B
blueswir1 已提交
1895
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
1896
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
1897 1898
        return;
    }
B
blueswir1 已提交
1899
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
B
Blue Swirl 已提交
1900
        FLOPPY_DPRINTF("error: controller not ready for writing\n");
B
bellard 已提交
1901 1902
        return;
    }
B
blueswir1 已提交
1903
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
bellard 已提交
1904
    /* Is it write command time ? */
B
blueswir1 已提交
1905
    if (fdctrl->msr & FD_MSR_NONDMA) {
B
bellard 已提交
1906
        /* FIFO data write */
1907 1908 1909 1910
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
1911
            fdctrl->data_pos == fdctrl->data_len) {
B
blueswir1 已提交
1912 1913
            cur_drv = get_cur_drv(fdctrl);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
B
Blue Swirl 已提交
1914 1915
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
B
blueswir1 已提交
1916 1917
                return;
            }
B
blueswir1 已提交
1918 1919 1920 1921 1922
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                FLOPPY_DPRINTF("error seeking to next sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
B
bellard 已提交
1923
        }
1924
        /* Switch from transfer mode to status mode
B
bellard 已提交
1925 1926
         * then from status mode to command mode
         */
B
blueswir1 已提交
1927
        if (fdctrl->data_pos == fdctrl->data_len)
1928
            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
B
bellard 已提交
1929 1930
        return;
    }
1931
    if (fdctrl->data_pos == 0) {
B
bellard 已提交
1932
        /* Command */
B
blueswir1 已提交
1933 1934 1935
        pos = command_to_handler[value & 0xff];
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        fdctrl->data_len = handlers[pos].parameters + 1;
1936
        fdctrl->msr |= FD_MSR_CMDBUSY;
B
bellard 已提交
1937
    }
B
blueswir1 已提交
1938

1939
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
B
blueswir1 已提交
1940 1941
    fdctrl->fifo[fdctrl->data_pos++] = value;
    if (fdctrl->data_pos == fdctrl->data_len) {
B
bellard 已提交
1942 1943 1944
        /* We now have all parameters
         * and will be able to treat the command
         */
1945 1946
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            fdctrl_format_sector(fdctrl);
B
bellard 已提交
1947 1948
            return;
        }
1949

B
blueswir1 已提交
1950 1951 1952
        pos = command_to_handler[fdctrl->fifo[0] & 0xff];
        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
B
bellard 已提交
1953 1954
    }
}
1955 1956 1957

static void fdctrl_result_timer(void *opaque)
{
B
Blue Swirl 已提交
1958 1959
    FDCtrl *fdctrl = opaque;
    FDrive *cur_drv = get_cur_drv(fdctrl);
1960

1961 1962 1963 1964 1965 1966 1967
    /* Pretend we are spinning.
     * This is needed for Coherent, which uses READ ID to check for
     * sector interleaving.
     */
    if (cur_drv->last_sect != 0) {
        cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
    }
1968 1969 1970 1971 1972 1973 1974 1975 1976
    /* READ_ID can't automatically succeed! */
    if (fdctrl->check_media_rate &&
        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
        FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
    } else {
        fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
    }
1977
}
B
blueswir1 已提交
1978

1979
static void fdctrl_change_cb(void *opaque, bool load)
1980 1981 1982 1983
{
    FDrive *drive = opaque;

    drive->media_changed = 1;
1984
    fd_revalidate(drive);
1985 1986 1987 1988 1989 1990
}

static const BlockDevOps fdctrl_block_ops = {
    .change_media_cb = fdctrl_change_cb,
};

B
blueswir1 已提交
1991
/* Init functions */
1992
static int fdctrl_connect_drives(FDCtrl *fdctrl)
B
blueswir1 已提交
1993
{
B
Blue Swirl 已提交
1994
    unsigned int i;
1995
    FDrive *drive;
B
blueswir1 已提交
1996 1997

    for (i = 0; i < MAX_FD; i++) {
1998
        drive = &fdctrl->drives[i];
1999
        drive->fdctrl = fdctrl;
2000

2001
        if (drive->bs) {
2002
            if (bdrv_get_on_error(drive->bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
2003 2004 2005
                error_report("fdc doesn't support drive option werror");
                return -1;
            }
2006
            if (bdrv_get_on_error(drive->bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
2007 2008 2009 2010 2011
                error_report("fdc doesn't support drive option rerror");
                return -1;
            }
        }

2012
        fd_init(drive);
P
Pavel Hrdina 已提交
2013
        fdctrl_change_cb(drive, 0);
2014
        if (drive->bs) {
2015
            bdrv_set_dev_ops(drive->bs, &fdctrl_block_ops, drive);
2016
        }
B
blueswir1 已提交
2017
    }
2018
    return 0;
B
blueswir1 已提交
2019 2020
}

M
Markus Armbruster 已提交
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040
ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
{
    ISADevice *dev;

    dev = isa_try_create(bus, "isa-fdc");
    if (!dev) {
        return NULL;
    }

    if (fds[0]) {
        qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv);
    }
    if (fds[1]) {
        qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv);
    }
    qdev_init_nofail(&dev->qdev);

    return dev;
}

B
Blue Swirl 已提交
2041
void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
A
Avi Kivity 已提交
2042
                        hwaddr mmio_base, DriveInfo **fds)
G
Gerd Hoffmann 已提交
2043
{
B
Blue Swirl 已提交
2044
    FDCtrl *fdctrl;
G
Gerd Hoffmann 已提交
2045
    DeviceState *dev;
B
Blue Swirl 已提交
2046
    FDCtrlSysBus *sys;
G
Gerd Hoffmann 已提交
2047 2048

    dev = qdev_create(NULL, "sysbus-fdc");
B
Blue Swirl 已提交
2049
    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
2050 2051
    fdctrl = &sys->state;
    fdctrl->dma_chann = dma_chann; /* FIXME */
2052
    if (fds[0]) {
2053
        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
2054 2055
    }
    if (fds[1]) {
2056
        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
2057
    }
M
Markus Armbruster 已提交
2058
    qdev_init_nofail(dev);
G
Gerd Hoffmann 已提交
2059 2060
    sysbus_connect_irq(&sys->busdev, 0, irq);
    sysbus_mmio_map(&sys->busdev, 0, mmio_base);
B
blueswir1 已提交
2061 2062
}

A
Avi Kivity 已提交
2063
void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
B
Blue Swirl 已提交
2064
                       DriveInfo **fds, qemu_irq *fdc_tc)
B
blueswir1 已提交
2065
{
B
Blue Swirl 已提交
2066
    DeviceState *dev;
B
Blue Swirl 已提交
2067
    FDCtrlSysBus *sys;
B
blueswir1 已提交
2068

B
Blue Swirl 已提交
2069
    dev = qdev_create(NULL, "SUNW,fdtwo");
2070
    if (fds[0]) {
2071
        qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
2072
    }
M
Markus Armbruster 已提交
2073
    qdev_init_nofail(dev);
B
Blue Swirl 已提交
2074
    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
G
Gerd Hoffmann 已提交
2075 2076
    sysbus_connect_irq(&sys->busdev, 0, irq);
    sysbus_mmio_map(&sys->busdev, 0, io_base);
B
Blue Swirl 已提交
2077
    *fdc_tc = qdev_get_gpio_in(dev, 0);
B
blueswir1 已提交
2078
}
B
Blue Swirl 已提交
2079

J
Jan Kiszka 已提交
2080
static int fdctrl_init_common(FDCtrl *fdctrl)
B
Blue Swirl 已提交
2081
{
B
Blue Swirl 已提交
2082 2083
    int i, j;
    static int command_tables_inited = 0;
B
Blue Swirl 已提交
2084

B
Blue Swirl 已提交
2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098
    /* Fill 'command_to_handler' lookup table */
    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;
                }
            }
        }
    }

    FLOPPY_DPRINTF("init controller\n");
    fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
J
Juan Quintela 已提交
2099
    fdctrl->fifo_size = 512;
2100
    fdctrl->result_timer = qemu_new_timer_ns(vm_clock,
B
Blue Swirl 已提交
2101 2102 2103 2104
                                          fdctrl_result_timer, fdctrl);

    fdctrl->version = 0x90; /* Intel 82078 controller */
    fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
J
Juan Quintela 已提交
2105
    fdctrl->num_floppies = MAX_FD;
B
Blue Swirl 已提交
2106

2107 2108
    if (fdctrl->dma_chann != -1)
        DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
2109
    return fdctrl_connect_drives(fdctrl);
B
Blue Swirl 已提交
2110 2111
}

2112
static const MemoryRegionPortio fdc_portio_list[] = {
2113
    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
2114 2115
    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
    PORTIO_END_OF_LIST(),
2116 2117
};

2118
static int isabus_fdc_init1(ISADevice *dev)
G
Gerd Hoffmann 已提交
2119
{
B
Blue Swirl 已提交
2120 2121
    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
    FDCtrl *fdctrl = &isa->state;
B
Blue Swirl 已提交
2122
    int ret;
G
Gerd Hoffmann 已提交
2123

2124
    isa_register_portio_list(dev, isa->iobase, fdc_portio_list, fdctrl, "fdc");
2125

2126 2127
    isa_init_irq(&isa->busdev, &fdctrl->irq, isa->irq);
    fdctrl->dma_chann = isa->dma;
G
Gerd Hoffmann 已提交
2128

2129
    qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 2);
J
Jan Kiszka 已提交
2130
    ret = fdctrl_init_common(fdctrl);
B
Blue Swirl 已提交
2131

2132 2133 2134
    add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0");
    add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1");

B
Blue Swirl 已提交
2135
    return ret;
G
Gerd Hoffmann 已提交
2136 2137
}

2138
static int sysbus_fdc_init1(SysBusDevice *dev)
B
Blue Swirl 已提交
2139
{
B
Blue Swirl 已提交
2140 2141
    FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev);
    FDCtrl *fdctrl = &sys->state;
B
Blue Swirl 已提交
2142
    int ret;
B
Blue Swirl 已提交
2143

A
Avi Kivity 已提交
2144
    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_ops, fdctrl, "fdc", 0x08);
2145
    sysbus_init_mmio(dev, &fdctrl->iomem);
G
Gerd Hoffmann 已提交
2146 2147
    sysbus_init_irq(dev, &fdctrl->irq);
    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
2148
    fdctrl->dma_chann = -1;
G
Gerd Hoffmann 已提交
2149

A
Avi Kivity 已提交
2150
    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
J
Jan Kiszka 已提交
2151
    ret = fdctrl_init_common(fdctrl);
B
Blue Swirl 已提交
2152 2153

    return ret;
B
Blue Swirl 已提交
2154 2155
}

2156
static int sun4m_fdc_init1(SysBusDevice *dev)
B
Blue Swirl 已提交
2157
{
B
Blue Swirl 已提交
2158
    FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state);
B
Blue Swirl 已提交
2159

A
Avi Kivity 已提交
2160 2161
    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_strict_ops, fdctrl,
                          "fdctrl", 0x08);
2162
    sysbus_init_mmio(dev, &fdctrl->iomem);
G
Gerd Hoffmann 已提交
2163 2164 2165 2166
    sysbus_init_irq(dev, &fdctrl->irq);
    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);

    fdctrl->sun4m = 1;
A
Avi Kivity 已提交
2167
    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
J
Jan Kiszka 已提交
2168
    return fdctrl_init_common(fdctrl);
B
Blue Swirl 已提交
2169
}
B
Blue Swirl 已提交
2170

2171
FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
K
Kevin Wolf 已提交
2172
{
2173
    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, fdc);
K
Kevin Wolf 已提交
2174

2175
    return isa->state.drives[i].drive;
K
Kevin Wolf 已提交
2176 2177
}

J
Jan Kiszka 已提交
2178 2179 2180 2181 2182 2183 2184 2185 2186 2187
static const VMStateDescription vmstate_isa_fdc ={
    .name = "fdc",
    .version_id = 2,
    .minimum_version_id = 2,
    .fields = (VMStateField []) {
        VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
        VMSTATE_END_OF_LIST()
    }
};

2188
static Property isa_fdc_properties[] = {
2189 2190 2191
    DEFINE_PROP_HEX32("iobase", FDCtrlISABus, iobase, 0x3f0),
    DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
    DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
2192 2193 2194 2195
    DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs),
    DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
    DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
    DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
2196 2197
    DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
                    0, true),
2198 2199 2200
    DEFINE_PROP_END_OF_LIST(),
};

2201 2202
static void isabus_fdc_class_init1(ObjectClass *klass, void *data)
{
2203
    DeviceClass *dc = DEVICE_CLASS(klass);
2204 2205
    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
    ic->init = isabus_fdc_init1;
2206 2207 2208 2209 2210 2211 2212
    dc->fw_name = "fdc";
    dc->no_user = 1;
    dc->reset = fdctrl_external_reset_isa;
    dc->vmsd = &vmstate_isa_fdc;
    dc->props = isa_fdc_properties;
}

2213
static const TypeInfo isa_fdc_info = {
2214 2215 2216 2217
    .name          = "isa-fdc",
    .parent        = TYPE_ISA_DEVICE,
    .instance_size = sizeof(FDCtrlISABus),
    .class_init    = isabus_fdc_class_init1,
G
Gerd Hoffmann 已提交
2218 2219
};

J
Jan Kiszka 已提交
2220 2221 2222 2223 2224 2225 2226 2227 2228 2229
static const VMStateDescription vmstate_sysbus_fdc ={
    .name = "fdc",
    .version_id = 2,
    .minimum_version_id = 2,
    .fields = (VMStateField []) {
        VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl),
        VMSTATE_END_OF_LIST()
    }
};

2230 2231 2232 2233
static Property sysbus_fdc_properties[] = {
    DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs),
    DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs),
    DEFINE_PROP_END_OF_LIST(),
B
Blue Swirl 已提交
2234 2235
};

2236 2237
static void sysbus_fdc_class_init(ObjectClass *klass, void *data)
{
2238
    DeviceClass *dc = DEVICE_CLASS(klass);
2239 2240 2241
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = sysbus_fdc_init1;
2242 2243 2244
    dc->reset = fdctrl_external_reset_sysbus;
    dc->vmsd = &vmstate_sysbus_fdc;
    dc->props = sysbus_fdc_properties;
2245 2246
}

2247
static const TypeInfo sysbus_fdc_info = {
2248 2249 2250 2251
    .name          = "sysbus-fdc",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(FDCtrlSysBus),
    .class_init    = sysbus_fdc_class_init,
2252 2253 2254 2255 2256 2257 2258 2259 2260
};

static Property sun4m_fdc_properties[] = {
    DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs),
    DEFINE_PROP_END_OF_LIST(),
};

static void sun4m_fdc_class_init(ObjectClass *klass, void *data)
{
2261
    DeviceClass *dc = DEVICE_CLASS(klass);
2262 2263 2264
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = sun4m_fdc_init1;
2265 2266 2267
    dc->reset = fdctrl_external_reset_sysbus;
    dc->vmsd = &vmstate_sysbus_fdc;
    dc->props = sun4m_fdc_properties;
2268 2269
}

2270
static const TypeInfo sun4m_fdc_info = {
2271 2272 2273 2274
    .name          = "SUNW,fdtwo",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(FDCtrlSysBus),
    .class_init    = sun4m_fdc_class_init,
B
Blue Swirl 已提交
2275 2276
};

A
Andreas Färber 已提交
2277
static void fdc_register_types(void)
B
Blue Swirl 已提交
2278
{
2279 2280 2281
    type_register_static(&isa_fdc_info);
    type_register_static(&sysbus_fdc_info);
    type_register_static(&sun4m_fdc_info);
B
Blue Swirl 已提交
2282 2283
}

A
Andreas Färber 已提交
2284
type_init(fdc_register_types)