fdc.c 65.1 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

P
pbrook 已提交
30 31
#include "hw.h"
#include "fdc.h"
32
#include "qemu-error.h"
P
pbrook 已提交
33 34
#include "qemu-timer.h"
#include "isa.h"
B
Blue Swirl 已提交
35
#include "sysbus.h"
B
Blue Swirl 已提交
36
#include "qdev-addr.h"
B
Blue Swirl 已提交
37
#include "blockdev.h"
38
#include "sysemu.h"
B
Blue Swirl 已提交
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                               */

B
blueswir1 已提交
55 56 57
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))

B
bellard 已提交
58
/* Will always be a fixed parameter for us */
59 60 61
#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 已提交
62

63 64
typedef struct FDCtrl FDCtrl;

B
bellard 已提交
65
/* Floppy disk drive emulation */
B
Blue Swirl 已提交
66
typedef enum FDiskFlags {
67
    FDISK_DBL_SIDES  = 0x01,
B
Blue Swirl 已提交
68
} FDiskFlags;
69

B
Blue Swirl 已提交
70
typedef struct FDrive {
71
    FDCtrl *fdctrl;
B
bellard 已提交
72 73
    BlockDriverState *bs;
    /* Drive status */
B
Blue Swirl 已提交
74
    FDriveType drive;
B
bellard 已提交
75 76 77 78 79 80
    uint8_t perpendicular;    /* 2.88 MB access mode    */
    /* Position */
    uint8_t head;
    uint8_t track;
    uint8_t sect;
    /* Media */
B
Blue Swirl 已提交
81
    FDiskFlags flags;
B
bellard 已提交
82 83
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
84
    uint16_t bps;             /* Bytes per sector       */
B
bellard 已提交
85
    uint8_t ro;               /* Is read-only           */
86
    uint8_t media_changed;    /* Is media changed       */
87
    uint8_t media_rate;       /* Data rate of medium    */
B
Blue Swirl 已提交
88
} FDrive;
B
bellard 已提交
89

B
Blue Swirl 已提交
90
static void fd_init(FDrive *drv)
B
bellard 已提交
91 92
{
    /* Drive */
B
bellard 已提交
93
    drv->drive = FDRIVE_DRV_NONE;
B
bellard 已提交
94 95
    drv->perpendicular = 0;
    /* Disk */
96
    drv->last_sect = 0;
B
bellard 已提交
97 98 99
    drv->max_track = 0;
}

100 101
#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)

B
Blue Swirl 已提交
102
static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
103
                          uint8_t last_sect, uint8_t num_sides)
B
bellard 已提交
104
{
105
    return (((track * num_sides) + head) * last_sect) + sect - 1;
B
bellard 已提交
106 107 108
}

/* Returns current position, in sectors, for given drive */
B
Blue Swirl 已提交
109
static int fd_sector(FDrive *drv)
B
bellard 已提交
110
{
111 112
    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
                          NUM_SIDES(drv));
B
bellard 已提交
113 114
}

B
blueswir1 已提交
115 116 117 118 119 120 121
/* 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 已提交
122 123
static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
                   int enable_seek)
B
bellard 已提交
124 125
{
    uint32_t sector;
126 127 128
    int ret;

    if (track > drv->max_track ||
129
        (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
130 131 132 133
        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 已提交
134 135 136
        return 2;
    }
    if (sect > drv->last_sect) {
137 138 139 140
        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 已提交
141 142
        return 3;
    }
143
    sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
144
    ret = 0;
B
bellard 已提交
145 146 147
    if (sector != fd_sector(drv)) {
#if 0
        if (!enable_seek) {
B
Blue Swirl 已提交
148 149 150 151
            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 已提交
152 153 154 155
            return 4;
        }
#endif
        drv->head = head;
156 157 158 159
        if (drv->track != track) {
            if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
                drv->media_changed = 0;
            }
160
            ret = 1;
161
        }
B
bellard 已提交
162 163 164 165
        drv->track = track;
        drv->sect = sect;
    }

166 167 168 169
    if (drv->bs == NULL || !bdrv_is_inserted(drv->bs)) {
        ret = 2;
    }

170
    return ret;
B
bellard 已提交
171 172 173
}

/* Set drive back to track 0 */
B
Blue Swirl 已提交
174
static void fd_recalibrate(FDrive *drv)
B
bellard 已提交
175 176
{
    FLOPPY_DPRINTF("recalibrate\n");
177
    fd_seek(drv, 0, 0, 1, 1);
B
bellard 已提交
178 179 180
}

/* Revalidate a disk drive after a disk change */
B
Blue Swirl 已提交
181
static void fd_revalidate(FDrive *drv)
B
bellard 已提交
182
{
183
    int nb_heads, max_track, last_sect, ro;
184
    FDriveType drive;
185
    FDriveRate rate;
B
bellard 已提交
186 187

    FLOPPY_DPRINTF("revalidate\n");
P
Pavel Hrdina 已提交
188
    if (drv->bs != NULL) {
189
        ro = bdrv_is_read_only(drv->bs);
190
        bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track,
191
                                      &last_sect, drv->drive, &drive, &rate);
P
Pavel Hrdina 已提交
192 193
        if (!bdrv_is_inserted(drv->bs)) {
            FLOPPY_DPRINTF("No disk in drive\n");
194
        } else {
195 196
            FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
                           max_track, last_sect, ro ? "ro" : "rw");
197 198 199 200 201 202 203 204 205
        }
        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;
206
        drv->drive = drive;
207
        drv->media_rate = rate;
B
bellard 已提交
208
    } else {
P
Pavel Hrdina 已提交
209
        FLOPPY_DPRINTF("No drive connected\n");
210
        drv->last_sect = 0;
211 212
        drv->max_track = 0;
        drv->flags &= ~FDISK_DBL_SIDES;
B
bellard 已提交
213
    }
214 215
}

B
bellard 已提交
216
/********************************************************/
B
bellard 已提交
217
/* Intel 82078 floppy disk controller emulation          */
B
bellard 已提交
218

B
Blue Swirl 已提交
219 220
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
static void fdctrl_reset_fifo(FDCtrl *fdctrl);
B
bellard 已提交
221
static int fdctrl_transfer_handler (void *opaque, int nchan,
A
Anthony Liguori 已提交
222
                                    int dma_pos, int dma_len);
B
Blue Swirl 已提交
223
static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0);
224
static FDrive *get_cur_drv(FDCtrl *fdctrl);
B
Blue Swirl 已提交
225 226 227 228 229 230 231 232 233 234 235 236

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);
237
static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
B
bellard 已提交
238 239 240 241 242 243 244 245 246 247

enum {
    FD_DIR_WRITE   = 0,
    FD_DIR_READ    = 1,
    FD_DIR_SCANE   = 2,
    FD_DIR_SCANL   = 3,
    FD_DIR_SCANH   = 4,
};

enum {
B
blueswir1 已提交
248 249 250
    FD_STATE_MULTI  = 0x01,	/* multi track flag */
    FD_STATE_FORMAT = 0x02,	/* format flag */
    FD_STATE_SEEK   = 0x04,	/* seek flag */
B
bellard 已提交
251 252
};

253
enum {
B
blueswir1 已提交
254 255
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
256 257 258 259 260 261
    FD_REG_DOR = 0x02,
    FD_REG_TDR = 0x03,
    FD_REG_MSR = 0x04,
    FD_REG_DSR = 0x04,
    FD_REG_FIFO = 0x05,
    FD_REG_DIR = 0x07,
262
    FD_REG_CCR = 0x07,
263 264 265
};

enum {
266
    FD_CMD_READ_TRACK = 0x02,
267 268
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
269 270
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
271 272
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
273 274 275 276
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
277 278 279
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
280
    FD_CMD_SCAN_EQUAL = 0x11,
281 282
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
283 284
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
285 286
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
287 288
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
289
    FD_CMD_SAVE = 0x2e,
290
    FD_CMD_OPTION = 0x33,
291
    FD_CMD_RESTORE = 0x4e,
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    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 已提交
307 308 309
    FD_SR0_DS0      = 0x01,
    FD_SR0_DS1      = 0x02,
    FD_SR0_HEAD     = 0x04,
310 311 312 313 314 315 316
    FD_SR0_EQPMT    = 0x10,
    FD_SR0_SEEK     = 0x20,
    FD_SR0_ABNTERM  = 0x40,
    FD_SR0_INVCMD   = 0x80,
    FD_SR0_RDYCHG   = 0xc0,
};

B
blueswir1 已提交
317
enum {
318
    FD_SR1_MA       = 0x01, /* Missing address mark */
319
    FD_SR1_NW       = 0x02, /* Not writable */
B
blueswir1 已提交
320 321 322 323 324 325 326 327
    FD_SR1_EC       = 0x80, /* End of cylinder */
};

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

B
blueswir1 已提交
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
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,
};

348
enum {
B
blueswir1 已提交
349 350 351
#if MAX_FD == 4
    FD_DOR_SELMASK  = 0x03,
#else
352
    FD_DOR_SELMASK  = 0x01,
B
blueswir1 已提交
353
#endif
354 355 356 357 358 359 360 361 362
    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 已提交
363
#if MAX_FD == 4
364
    FD_TDR_BOOTSEL  = 0x0c,
B
blueswir1 已提交
365 366 367
#else
    FD_TDR_BOOTSEL  = 0x04,
#endif
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
};

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 已提交
391 392
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
393
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
B
bellard 已提交
394

B
Blue Swirl 已提交
395
struct FDCtrl {
A
Avi Kivity 已提交
396
    MemoryRegion iomem;
P
pbrook 已提交
397
    qemu_irq irq;
B
bellard 已提交
398
    /* Controller state */
399
    QEMUTimer *result_timer;
400 401 402 403
    int dma_chann;
    /* Controller's identification */
    uint8_t version;
    /* HW */
B
blueswir1 已提交
404 405
    uint8_t sra;
    uint8_t srb;
B
blueswir1 已提交
406
    uint8_t dor;
J
Juan Quintela 已提交
407
    uint8_t dor_vmstate; /* only used as temp during vmstate */
B
blueswir1 已提交
408
    uint8_t tdr;
B
blueswir1 已提交
409
    uint8_t dsr;
B
blueswir1 已提交
410
    uint8_t msr;
B
bellard 已提交
411
    uint8_t cur_drv;
B
blueswir1 已提交
412 413 414
    uint8_t status0;
    uint8_t status1;
    uint8_t status2;
B
bellard 已提交
415
    /* Command FIFO */
416
    uint8_t *fifo;
J
Juan Quintela 已提交
417
    int32_t fifo_size;
B
bellard 已提交
418 419 420 421
    uint32_t data_pos;
    uint32_t data_len;
    uint8_t data_state;
    uint8_t data_dir;
422
    uint8_t eot; /* last wanted sector */
B
bellard 已提交
423 424 425 426 427 428 429 430
    /* 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 已提交
431
    uint8_t num_floppies;
432 433
    /* Sun4m quirks? */
    int sun4m;
B
Blue Swirl 已提交
434
    FDrive drives[MAX_FD];
435
    int reset_sensei;
436
    uint32_t check_media_rate;
437 438 439
    /* Timers state */
    uint8_t timer0;
    uint8_t timer1;
440 441
};

B
Blue Swirl 已提交
442
typedef struct FDCtrlSysBus {
G
Gerd Hoffmann 已提交
443
    SysBusDevice busdev;
B
Blue Swirl 已提交
444 445
    struct FDCtrl state;
} FDCtrlSysBus;
G
Gerd Hoffmann 已提交
446

B
Blue Swirl 已提交
447
typedef struct FDCtrlISABus {
G
Gerd Hoffmann 已提交
448
    ISADevice busdev;
449 450 451
    uint32_t iobase;
    uint32_t irq;
    uint32_t dma;
B
Blue Swirl 已提交
452
    struct FDCtrl state;
453 454
    int32_t bootindexA;
    int32_t bootindexB;
B
Blue Swirl 已提交
455
} FDCtrlISABus;
G
Gerd Hoffmann 已提交
456

457 458
static uint32_t fdctrl_read (void *opaque, uint32_t reg)
{
B
Blue Swirl 已提交
459
    FDCtrl *fdctrl = opaque;
460 461
    uint32_t retval;

K
Kevin Wolf 已提交
462
    reg &= 7;
B
blueswir1 已提交
463
    switch (reg) {
B
blueswir1 已提交
464 465
    case FD_REG_SRA:
        retval = fdctrl_read_statusA(fdctrl);
466
        break;
B
blueswir1 已提交
467
    case FD_REG_SRB:
468 469
        retval = fdctrl_read_statusB(fdctrl);
        break;
470
    case FD_REG_DOR:
471 472
        retval = fdctrl_read_dor(fdctrl);
        break;
473
    case FD_REG_TDR:
474
        retval = fdctrl_read_tape(fdctrl);
475
        break;
476
    case FD_REG_MSR:
477
        retval = fdctrl_read_main_status(fdctrl);
478
        break;
479
    case FD_REG_FIFO:
480
        retval = fdctrl_read_data(fdctrl);
481
        break;
482
    case FD_REG_DIR:
483
        retval = fdctrl_read_dir(fdctrl);
484
        break;
485
    default:
486 487
        retval = (uint32_t)(-1);
        break;
488
    }
489
    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
490 491 492 493 494 495

    return retval;
}

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

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

K
Kevin Wolf 已提交
500
    reg &= 7;
B
blueswir1 已提交
501
    switch (reg) {
502
    case FD_REG_DOR:
503 504
        fdctrl_write_dor(fdctrl, value);
        break;
505
    case FD_REG_TDR:
506
        fdctrl_write_tape(fdctrl, value);
507
        break;
508
    case FD_REG_DSR:
509
        fdctrl_write_rate(fdctrl, value);
510
        break;
511
    case FD_REG_FIFO:
512
        fdctrl_write_data(fdctrl, value);
513
        break;
514 515 516
    case FD_REG_CCR:
        fdctrl_write_ccr(fdctrl, value);
        break;
517
    default:
518
        break;
519
    }
520 521
}

A
Avi Kivity 已提交
522 523
static uint64_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg,
                                 unsigned ize)
B
bellard 已提交
524
{
525
    return fdctrl_read(opaque, (uint32_t)reg);
B
bellard 已提交
526 527
}

A
Avi Kivity 已提交
528 529
static void fdctrl_write_mem (void *opaque, target_phys_addr_t reg,
                              uint64_t value, unsigned size)
B
bellard 已提交
530
{
531
    fdctrl_write(opaque, (uint32_t)reg, value);
B
bellard 已提交
532 533
}

A
Avi Kivity 已提交
534 535 536 537
static const MemoryRegionOps fdctrl_mem_ops = {
    .read = fdctrl_read_mem,
    .write = fdctrl_write_mem,
    .endianness = DEVICE_NATIVE_ENDIAN,
B
bellard 已提交
538 539
};

A
Avi Kivity 已提交
540 541 542 543 544 545 546 547
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,
    },
548 549
};

550 551 552 553
static bool fdrive_media_changed_needed(void *opaque)
{
    FDrive *drive = opaque;

554
    return (drive->bs != NULL && drive->media_changed != 1);
555 556 557 558 559 560 561 562 563 564 565 566 567
}

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()
    }
};

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
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 已提交
586 587 588 589 590
static const VMStateDescription vmstate_fdrive = {
    .name = "fdrive",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
591
    .fields      = (VMStateField[]) {
B
Blue Swirl 已提交
592 593 594
        VMSTATE_UINT8(head, FDrive),
        VMSTATE_UINT8(track, FDrive),
        VMSTATE_UINT8(sect, FDrive),
J
Juan Quintela 已提交
595
        VMSTATE_END_OF_LIST()
596 597 598 599 600
    },
    .subsections = (VMStateSubsection[]) {
        {
            .vmsd = &vmstate_fdrive_media_changed,
            .needed = &fdrive_media_changed_needed,
601 602 603
        } , {
            .vmsd = &vmstate_fdrive_media_rate,
            .needed = &fdrive_media_rate_needed,
604 605 606
        } , {
            /* empty */
        }
J
Juan Quintela 已提交
607 608
    }
};
609

610
static void fdc_pre_save(void *opaque)
611
{
B
Blue Swirl 已提交
612
    FDCtrl *s = opaque;
613

J
Juan Quintela 已提交
614
    s->dor_vmstate = s->dor | GET_CUR_DRV(s);
615 616
}

617
static int fdc_post_load(void *opaque, int version_id)
618
{
B
Blue Swirl 已提交
619
    FDCtrl *s = opaque;
620

J
Juan Quintela 已提交
621 622
    SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
    s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
623 624 625
    return 0;
}

J
Juan Quintela 已提交
626
static const VMStateDescription vmstate_fdc = {
627
    .name = "fdc",
J
Juan Quintela 已提交
628 629 630 631 632 633 634
    .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 已提交
635 636 637 638 639 640 641 642 643
        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 已提交
644
        /* Command FIFO */
B
Blue Swirl 已提交
645 646
        VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8,
                             uint8_t),
B
Blue Swirl 已提交
647 648 649 650 651
        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 已提交
652
        /* States kept only to be returned back */
B
Blue Swirl 已提交
653 654 655 656 657 658 659 660 661
        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 已提交
662
        VMSTATE_END_OF_LIST()
B
blueswir1 已提交
663
    }
J
Juan Quintela 已提交
664
};
665

B
Blue Swirl 已提交
666
static void fdctrl_external_reset_sysbus(DeviceState *d)
667
{
B
Blue Swirl 已提交
668 669
    FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev);
    FDCtrl *s = &sys->state;
B
Blue Swirl 已提交
670 671 672 673 674 675

    fdctrl_reset(s, 0);
}

static void fdctrl_external_reset_isa(DeviceState *d)
{
B
Blue Swirl 已提交
676 677
    FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev);
    FDCtrl *s = &isa->state;
678 679 680 681

    fdctrl_reset(s, 0);
}

B
blueswir1 已提交
682 683
static void fdctrl_handle_tc(void *opaque, int irq, int level)
{
B
Blue Swirl 已提交
684
    //FDCtrl *s = opaque;
B
blueswir1 已提交
685 686 687 688 689 690 691

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

B
bellard 已提交
692
/* Change IRQ state */
B
Blue Swirl 已提交
693
static void fdctrl_reset_irq(FDCtrl *fdctrl)
B
bellard 已提交
694
{
B
blueswir1 已提交
695 696
    if (!(fdctrl->sra & FD_SRA_INTPEND))
        return;
697
    FLOPPY_DPRINTF("Reset interrupt\n");
P
pbrook 已提交
698
    qemu_set_irq(fdctrl->irq, 0);
B
blueswir1 已提交
699
    fdctrl->sra &= ~FD_SRA_INTPEND;
B
bellard 已提交
700 701
}

B
Blue Swirl 已提交
702
static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0)
B
bellard 已提交
703
{
B
blueswir1 已提交
704 705 706 707 708
    /* 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;
B
blueswir1 已提交
709
        fdctrl->status0 = status0;
710
        return;
B
bellard 已提交
711
    }
B
blueswir1 已提交
712
    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
P
pbrook 已提交
713
        qemu_set_irq(fdctrl->irq, 1);
B
blueswir1 已提交
714
        fdctrl->sra |= FD_SRA_INTPEND;
B
bellard 已提交
715
    }
716

717
    fdctrl->reset_sensei = 0;
B
blueswir1 已提交
718 719
    fdctrl->status0 = status0;
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
B
bellard 已提交
720 721
}

B
bellard 已提交
722
/* Reset controller */
B
Blue Swirl 已提交
723
static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
B
bellard 已提交
724 725 726
{
    int i;

B
bellard 已提交
727
    FLOPPY_DPRINTF("reset controller\n");
728
    fdctrl_reset_irq(fdctrl);
B
bellard 已提交
729
    /* Initialise controller */
B
blueswir1 已提交
730 731 732 733
    fdctrl->sra = 0;
    fdctrl->srb = 0xc0;
    if (!fdctrl->drives[1].bs)
        fdctrl->sra |= FD_SRA_nDRV2;
734
    fdctrl->cur_drv = 0;
B
blueswir1 已提交
735
    fdctrl->dor = FD_DOR_nRESET;
B
blueswir1 已提交
736
    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
B
blueswir1 已提交
737
    fdctrl->msr = FD_MSR_RQM;
B
bellard 已提交
738
    /* FIFO state */
739 740
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
B
blueswir1 已提交
741
    fdctrl->data_state = 0;
742
    fdctrl->data_dir = FD_DIR_WRITE;
B
bellard 已提交
743
    for (i = 0; i < MAX_FD; i++)
B
blueswir1 已提交
744
        fd_recalibrate(&fdctrl->drives[i]);
745
    fdctrl_reset_fifo(fdctrl);
B
blueswir1 已提交
746
    if (do_irq) {
747
        fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
748
        fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
B
blueswir1 已提交
749
    }
750 751
}

B
Blue Swirl 已提交
752
static inline FDrive *drv0(FDCtrl *fdctrl)
753
{
B
blueswir1 已提交
754
    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
755 756
}

B
Blue Swirl 已提交
757
static inline FDrive *drv1(FDCtrl *fdctrl)
758
{
B
blueswir1 已提交
759 760 761 762
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
        return &fdctrl->drives[1];
    else
        return &fdctrl->drives[0];
763 764
}

B
blueswir1 已提交
765
#if MAX_FD == 4
B
Blue Swirl 已提交
766
static inline FDrive *drv2(FDCtrl *fdctrl)
B
blueswir1 已提交
767 768 769 770 771 772 773
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
        return &fdctrl->drives[2];
    else
        return &fdctrl->drives[1];
}

B
Blue Swirl 已提交
774
static inline FDrive *drv3(FDCtrl *fdctrl)
B
blueswir1 已提交
775 776 777 778 779 780 781 782
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
        return &fdctrl->drives[3];
    else
        return &fdctrl->drives[2];
}
#endif

B
Blue Swirl 已提交
783
static FDrive *get_cur_drv(FDCtrl *fdctrl)
784
{
B
blueswir1 已提交
785 786 787 788 789 790 791 792 793
    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 已提交
794 795
}

B
blueswir1 已提交
796
/* Status A register : 0x00 (read-only) */
B
Blue Swirl 已提交
797
static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
B
blueswir1 已提交
798 799 800 801 802 803 804 805
{
    uint32_t retval = fdctrl->sra;

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

    return retval;
}

B
bellard 已提交
806
/* Status B register : 0x01 (read-only) */
B
Blue Swirl 已提交
807
static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl)
B
bellard 已提交
808
{
B
blueswir1 已提交
809 810 811 812 813
    uint32_t retval = fdctrl->srb;

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

    return retval;
B
bellard 已提交
814 815 816
}

/* Digital output register : 0x02 */
B
Blue Swirl 已提交
817
static uint32_t fdctrl_read_dor(FDCtrl *fdctrl)
B
bellard 已提交
818
{
B
blueswir1 已提交
819
    uint32_t retval = fdctrl->dor;
B
bellard 已提交
820 821

    /* Selected drive */
822
    retval |= fdctrl->cur_drv;
B
bellard 已提交
823 824 825 826 827
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}

B
Blue Swirl 已提交
828
static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
829 830
{
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
B
blueswir1 已提交
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847

    /* 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 已提交
848
    /* Reset */
849
    if (!(value & FD_DOR_nRESET)) {
B
blueswir1 已提交
850
        if (fdctrl->dor & FD_DOR_nRESET) {
B
bellard 已提交
851
            FLOPPY_DPRINTF("controller enter RESET state\n");
B
bellard 已提交
852 853
        }
    } else {
B
blueswir1 已提交
854
        if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
855
            FLOPPY_DPRINTF("controller out of RESET state\n");
856
            fdctrl_reset(fdctrl, 1);
B
blueswir1 已提交
857
            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
bellard 已提交
858 859 860
        }
    }
    /* Selected drive */
861
    fdctrl->cur_drv = value & FD_DOR_SELMASK;
B
blueswir1 已提交
862 863

    fdctrl->dor = value;
B
bellard 已提交
864 865 866
}

/* Tape drive register : 0x03 */
B
Blue Swirl 已提交
867
static uint32_t fdctrl_read_tape(FDCtrl *fdctrl)
B
bellard 已提交
868
{
B
blueswir1 已提交
869
    uint32_t retval = fdctrl->tdr;
B
bellard 已提交
870 871 872 873 874 875

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

    return retval;
}

B
Blue Swirl 已提交
876
static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
877 878
{
    /* Reset mode */
B
blueswir1 已提交
879
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
880
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
881 882 883 884
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
B
blueswir1 已提交
885
    fdctrl->tdr = value & FD_TDR_BOOTSEL;
B
bellard 已提交
886 887 888 889
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
B
Blue Swirl 已提交
890
static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
B
bellard 已提交
891
{
B
blueswir1 已提交
892
    uint32_t retval = fdctrl->msr;
B
bellard 已提交
893

B
blueswir1 已提交
894
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
blueswir1 已提交
895
    fdctrl->dor |= FD_DOR_nRESET;
B
blueswir1 已提交
896

897 898 899 900 901 902
    /* Sparc mutation */
    if (fdctrl->sun4m) {
        retval |= FD_MSR_DIO;
        fdctrl_reset_irq(fdctrl);
    };

B
bellard 已提交
903 904 905 906 907 908
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
B
Blue Swirl 已提交
909
static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
B
bellard 已提交
910 911
{
    /* Reset mode */
B
blueswir1 已提交
912
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
913 914 915
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
B
bellard 已提交
916 917
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
918
    if (value & FD_DSR_SWRESET) {
B
blueswir1 已提交
919
        fdctrl->dor &= ~FD_DOR_nRESET;
920
        fdctrl_reset(fdctrl, 1);
B
blueswir1 已提交
921
        fdctrl->dor |= FD_DOR_nRESET;
B
bellard 已提交
922
    }
923
    if (value & FD_DSR_PWRDOWN) {
924
        fdctrl_reset(fdctrl, 1);
B
bellard 已提交
925
    }
B
blueswir1 已提交
926
    fdctrl->dsr = value;
B
bellard 已提交
927 928
}

929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
/* 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 已提交
946
static int fdctrl_media_changed(FDrive *drv)
B
bellard 已提交
947
{
948
    return drv->media_changed;
B
bellard 已提交
949 950
}

B
bellard 已提交
951
/* Digital input register : 0x07 (read-only) */
B
Blue Swirl 已提交
952
static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
B
bellard 已提交
953 954 955
{
    uint32_t retval = 0;

956
    if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
957
        retval |= FD_DIR_DSKCHG;
958
    }
959
    if (retval != 0) {
960
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
961
    }
B
bellard 已提交
962 963 964 965 966

    return retval;
}

/* FIFO state control */
B
Blue Swirl 已提交
967
static void fdctrl_reset_fifo(FDCtrl *fdctrl)
B
bellard 已提交
968
{
969 970
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
B
blueswir1 已提交
971
    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
B
bellard 已提交
972 973 974
}

/* Set FIFO status for the host to read */
P
Pavel Hrdina 已提交
975
static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, uint8_t status0)
B
bellard 已提交
976
{
977 978 979
    fdctrl->data_dir = FD_DIR_READ;
    fdctrl->data_len = fifo_len;
    fdctrl->data_pos = 0;
B
blueswir1 已提交
980
    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
P
Pavel Hrdina 已提交
981 982 983
    if (status0) {
        fdctrl_raise_irq(fdctrl, status0);
    }
B
bellard 已提交
984 985 986
}

/* Set an error: unimplemented/unknown command */
B
Blue Swirl 已提交
987
static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
B
bellard 已提交
988
{
B
Blue Swirl 已提交
989 990
    qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
                  fdctrl->fifo[0]);
991
    fdctrl->fifo[0] = FD_SR0_INVCMD;
992
    fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
993 994
}

995 996 997 998
/* Seek to next sector
 * returns 0 when end of track reached (for DBL_SIDES on head 1)
 * otherwise returns 1
 */
B
Blue Swirl 已提交
999
static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
B
blueswir1 已提交
1000 1001 1002 1003 1004 1005
{
    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 */
1006 1007 1008 1009 1010 1011 1012 1013 1014
    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 已提交
1015
        if (FD_MULTI_TRACK(fdctrl->data_state)) {
1016
            if (new_head == 0 &&
B
blueswir1 已提交
1017
                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1018
                new_head = 1;
B
blueswir1 已提交
1019
            } else {
1020 1021 1022 1023 1024
                new_head = 0;
                new_track++;
                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
                    ret = 0;
                }
B
blueswir1 已提交
1025 1026
            }
        } else {
1027 1028 1029 1030 1031 1032
            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 已提交
1033 1034
        }
    } else {
1035
        new_sect++;
B
blueswir1 已提交
1036
    }
1037 1038
    fd_seek(cur_drv, new_head, new_track, new_sect, 1);
    return ret;
B
blueswir1 已提交
1039 1040
}

B
bellard 已提交
1041
/* Callback for transfer end (stop or abort) */
B
Blue Swirl 已提交
1042 1043
static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
                                 uint8_t status1, uint8_t status2)
B
bellard 已提交
1044
{
B
Blue Swirl 已提交
1045
    FDrive *cur_drv;
B
bellard 已提交
1046

1047
    cur_drv = get_cur_drv(fdctrl);
P
Pavel Hrdina 已提交
1048 1049 1050
    fdctrl->status0 = status0 | FD_SR0_SEEK | (cur_drv->head << 2) |
                      GET_CUR_DRV(fdctrl);

B
bellard 已提交
1051
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
P
Pavel Hrdina 已提交
1052 1053
                   status0, status1, status2, fdctrl->status0);
    fdctrl->fifo[0] = fdctrl->status0;
1054 1055 1056 1057 1058 1059 1060
    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 已提交
1061
    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1062
        DMA_release_DREQ(fdctrl->dma_chann);
1063
    }
B
blueswir1 已提交
1064
    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
B
blueswir1 已提交
1065
    fdctrl->msr &= ~FD_MSR_NONDMA;
P
Pavel Hrdina 已提交
1066
    fdctrl_set_fifo(fdctrl, 7, fdctrl->status0);
B
bellard 已提交
1067 1068 1069
}

/* Prepare a data transfer (either DMA or FIFO) */
B
Blue Swirl 已提交
1070
static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
B
bellard 已提交
1071
{
B
Blue Swirl 已提交
1072
    FDrive *cur_drv;
B
bellard 已提交
1073
    uint8_t kh, kt, ks;
B
blueswir1 已提交
1074
    int did_seek = 0;
B
bellard 已提交
1075

B
blueswir1 已提交
1076
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1077 1078 1079 1080
    cur_drv = get_cur_drv(fdctrl);
    kt = fdctrl->fifo[2];
    kh = fdctrl->fifo[3];
    ks = fdctrl->fifo[4];
B
bellard 已提交
1081
    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
B
blueswir1 已提交
1082
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1083 1084
                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
                                  NUM_SIDES(cur_drv)));
B
blueswir1 已提交
1085
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
B
bellard 已提交
1086 1087
    case 2:
        /* sect too big */
1088
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1089 1090 1091
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1092 1093 1094
        return;
    case 3:
        /* track too big */
B
blueswir1 已提交
1095
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1096 1097 1098
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1099 1100 1101
        return;
    case 4:
        /* No seek enabled */
1102
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1103 1104 1105
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
1106 1107 1108 1109 1110 1111 1112
        return;
    case 1:
        did_seek = 1;
        break;
    default:
        break;
    }
B
blueswir1 已提交
1113

1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
    /* 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 已提交
1127
    /* Set the FIFO state */
1128 1129
    fdctrl->data_dir = direction;
    fdctrl->data_pos = 0;
B
blueswir1 已提交
1130
    fdctrl->msr |= FD_MSR_CMDBUSY;
1131 1132 1133 1134
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
B
bellard 已提交
1135
    if (did_seek)
1136 1137 1138 1139 1140 1141
        fdctrl->data_state |= FD_STATE_SEEK;
    else
        fdctrl->data_state &= ~FD_STATE_SEEK;
    if (fdctrl->fifo[5] == 00) {
        fdctrl->data_len = fdctrl->fifo[8];
    } else {
1142
        int tmp;
T
ths 已提交
1143
        fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
1144
        tmp = (fdctrl->fifo[6] - ks + 1);
1145
        if (fdctrl->fifo[0] & 0x80)
1146
            tmp += fdctrl->fifo[6];
1147
        fdctrl->data_len *= tmp;
1148
    }
1149
    fdctrl->eot = fdctrl->fifo[6];
B
blueswir1 已提交
1150
    if (fdctrl->dor & FD_DOR_DMAEN) {
B
bellard 已提交
1151 1152
        int dma_mode;
        /* DMA transfer are enabled. Check if DMA channel is well programmed */
1153
        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
B
bellard 已提交
1154
        dma_mode = (dma_mode >> 2) & 3;
1155
        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
1156
                       dma_mode, direction,
1157
                       (128 << fdctrl->fifo[5]) *
1158
                       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
B
bellard 已提交
1159 1160 1161 1162 1163
        if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
              direction == FD_DIR_SCANH) && dma_mode == 0) ||
            (direction == FD_DIR_WRITE && dma_mode == 2) ||
            (direction == FD_DIR_READ && dma_mode == 1)) {
            /* No access is allowed until DMA transfer has completed */
B
blueswir1 已提交
1164
            fdctrl->msr &= ~FD_MSR_RQM;
B
bellard 已提交
1165
            /* Now, we just have to wait for the DMA controller to
B
bellard 已提交
1166 1167
             * recall us...
             */
1168 1169
            DMA_hold_DREQ(fdctrl->dma_chann);
            DMA_schedule(fdctrl->dma_chann);
B
bellard 已提交
1170
            return;
1171
        } else {
B
Blue Swirl 已提交
1172 1173
            FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode,
                           direction);
B
bellard 已提交
1174 1175 1176
        }
    }
    FLOPPY_DPRINTF("start non-DMA transfer\n");
B
blueswir1 已提交
1177
    fdctrl->msr |= FD_MSR_NONDMA;
B
blueswir1 已提交
1178 1179
    if (direction != FD_DIR_WRITE)
        fdctrl->msr |= FD_MSR_DIO;
B
bellard 已提交
1180
    /* IO based transfer: calculate len */
P
Pavel Hrdina 已提交
1181
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
B
bellard 已提交
1182 1183 1184 1185 1186

    return;
}

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

B
bellard 已提交
1191 1192 1193
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
1194
    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
B
bellard 已提交
1195 1196 1197
}

/* handlers for DMA transfers */
B
bellard 已提交
1198 1199
static int fdctrl_transfer_handler (void *opaque, int nchan,
                                    int dma_pos, int dma_len)
B
bellard 已提交
1200
{
B
Blue Swirl 已提交
1201 1202
    FDCtrl *fdctrl;
    FDrive *cur_drv;
1203
    int len, start_pos, rel_pos;
B
bellard 已提交
1204 1205
    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;

1206
    fdctrl = opaque;
B
blueswir1 已提交
1207
    if (fdctrl->msr & FD_MSR_RQM) {
B
bellard 已提交
1208 1209 1210
        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
        return 0;
    }
1211 1212 1213
    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 已提交
1214
        status2 = FD_SR2_SNS;
B
bellard 已提交
1215 1216
    if (dma_len > fdctrl->data_len)
        dma_len = fdctrl->data_len;
1217
    if (cur_drv->bs == NULL) {
1218
        if (fdctrl->data_dir == FD_DIR_WRITE)
1219
            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1220
        else
1221
            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1222
        len = 0;
1223 1224
        goto transfer_error;
    }
1225
    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
B
bellard 已提交
1226 1227
    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
        len = dma_len - fdctrl->data_pos;
1228 1229
        if (len + rel_pos > FD_SECTOR_LEN)
            len = FD_SECTOR_LEN - rel_pos;
B
bellard 已提交
1230 1231
        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 已提交
1232
                       fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
1233
                       cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
1234
                       fd_sector(cur_drv) * FD_SECTOR_LEN);
1235
        if (fdctrl->data_dir != FD_DIR_WRITE ||
1236
            len < FD_SECTOR_LEN || rel_pos != 0) {
1237 1238
            /* READ & SCAN commands and realign to a sector for WRITE */
            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1239
                          fdctrl->fifo, 1) < 0) {
B
bellard 已提交
1240 1241 1242
                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
                               fd_sector(cur_drv));
                /* Sure, image size is too small... */
1243
                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
B
bellard 已提交
1244
            }
1245
        }
1246 1247 1248
        switch (fdctrl->data_dir) {
        case FD_DIR_READ:
            /* READ commands */
B
bellard 已提交
1249 1250
            DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
                              fdctrl->data_pos, len);
1251 1252
            break;
        case FD_DIR_WRITE:
1253
            /* WRITE commands */
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
            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 已提交
1264 1265
            DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
                             fdctrl->data_pos, len);
1266
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1267
                           fdctrl->fifo, 1) < 0) {
B
Blue Swirl 已提交
1268 1269
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
1270
                fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1271
                goto transfer_error;
1272
            }
1273 1274 1275
            break;
        default:
            /* SCAN commands */
1276
            {
1277
                uint8_t tmpbuf[FD_SECTOR_LEN];
1278
                int ret;
B
bellard 已提交
1279
                DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
1280
                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
B
bellard 已提交
1281
                if (ret == 0) {
B
blueswir1 已提交
1282
                    status2 = FD_SR2_SEH;
B
bellard 已提交
1283 1284
                    goto end_transfer;
                }
1285 1286
                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
B
bellard 已提交
1287 1288 1289 1290
                    status2 = 0x00;
                    goto end_transfer;
                }
            }
1291
            break;
B
bellard 已提交
1292
        }
1293 1294
        fdctrl->data_pos += len;
        rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1295
        if (rel_pos == 0) {
B
bellard 已提交
1296
            /* Seek to next sector */
B
blueswir1 已提交
1297 1298
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
                break;
B
bellard 已提交
1299 1300
        }
    }
1301
 end_transfer:
1302 1303
    len = fdctrl->data_pos - start_pos;
    FLOPPY_DPRINTF("end transfer %d %d %d\n",
1304
                   fdctrl->data_pos, len, fdctrl->data_len);
1305 1306 1307
    if (fdctrl->data_dir == FD_DIR_SCANE ||
        fdctrl->data_dir == FD_DIR_SCANL ||
        fdctrl->data_dir == FD_DIR_SCANH)
B
blueswir1 已提交
1308
        status2 = FD_SR2_SEH;
1309
    if (FD_DID_SEEK(fdctrl->data_state))
1310
        status0 |= FD_SR0_SEEK;
1311
    fdctrl->data_len -= len;
1312
    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1313
 transfer_error:
B
bellard 已提交
1314

1315
    return len;
B
bellard 已提交
1316 1317 1318
}

/* Data register : 0x05 */
B
Blue Swirl 已提交
1319
static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
B
bellard 已提交
1320
{
B
Blue Swirl 已提交
1321
    FDrive *cur_drv;
B
bellard 已提交
1322
    uint32_t retval = 0;
B
blueswir1 已提交
1323
    int pos;
B
bellard 已提交
1324

1325
    cur_drv = get_cur_drv(fdctrl);
B
blueswir1 已提交
1326 1327
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
    if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
B
Blue Swirl 已提交
1328
        FLOPPY_DPRINTF("error: controller not ready for reading\n");
B
bellard 已提交
1329 1330
        return 0;
    }
1331
    pos = fdctrl->data_pos;
B
blueswir1 已提交
1332
    if (fdctrl->msr & FD_MSR_NONDMA) {
B
bellard 已提交
1333 1334
        pos %= FD_SECTOR_LEN;
        if (pos == 0) {
B
blueswir1 已提交
1335 1336 1337 1338 1339 1340
            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 已提交
1341 1342 1343 1344 1345 1346
            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 已提交
1347 1348
        }
    }
1349 1350 1351
    retval = fdctrl->fifo[pos];
    if (++fdctrl->data_pos == fdctrl->data_len) {
        fdctrl->data_pos = 0;
1352
        /* Switch from transfer mode to status mode
B
bellard 已提交
1353 1354
         * then from status mode to command mode
         */
B
blueswir1 已提交
1355
        if (fdctrl->msr & FD_MSR_NONDMA) {
1356
            fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1357
        } else {
1358
            fdctrl_reset_fifo(fdctrl);
1359 1360
            fdctrl_reset_irq(fdctrl);
        }
B
bellard 已提交
1361 1362 1363 1364 1365 1366
    }
    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);

    return retval;
}

B
Blue Swirl 已提交
1367
static void fdctrl_format_sector(FDCtrl *fdctrl)
B
bellard 已提交
1368
{
B
Blue Swirl 已提交
1369
    FDrive *cur_drv;
1370
    uint8_t kh, kt, ks;
B
bellard 已提交
1371

B
blueswir1 已提交
1372
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1373 1374 1375 1376 1377
    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 已提交
1378
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1379 1380
                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
                                  NUM_SIDES(cur_drv)));
1381
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1382 1383
    case 2:
        /* sect too big */
1384
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1385 1386 1387 1388 1389 1390
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
B
blueswir1 已提交
1391
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1392 1393 1394 1395 1396 1397
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
1398
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 1:
        fdctrl->data_state |= FD_STATE_SEEK;
        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 已提交
1412
        FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv));
1413
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1414
    } else {
1415 1416 1417 1418
        if (cur_drv->sect == cur_drv->last_sect) {
            fdctrl->data_state &= ~FD_STATE_FORMAT;
            /* Last sector done */
            if (FD_DID_SEEK(fdctrl->data_state))
1419
                fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1420 1421 1422 1423 1424 1425 1426
            else
                fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        } else {
            /* More to do */
            fdctrl->data_pos = 0;
            fdctrl->data_len = 4;
        }
1427 1428 1429
    }
}

B
Blue Swirl 已提交
1430
static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
1431 1432 1433
{
    fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
    fdctrl->fifo[0] = fdctrl->lock << 4;
1434
    fdctrl_set_fifo(fdctrl, 1, 0);
1435 1436
}

B
Blue Swirl 已提交
1437
static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
1438
{
B
Blue Swirl 已提交
1439
    FDrive *cur_drv = get_cur_drv(fdctrl);
1440 1441 1442 1443

    /* Drives position */
    fdctrl->fifo[0] = drv0(fdctrl)->track;
    fdctrl->fifo[1] = drv1(fdctrl)->track;
B
blueswir1 已提交
1444 1445 1446 1447
#if MAX_FD == 4
    fdctrl->fifo[2] = drv2(fdctrl)->track;
    fdctrl->fifo[3] = drv3(fdctrl)->track;
#else
1448 1449
    fdctrl->fifo[2] = 0;
    fdctrl->fifo[3] = 0;
B
blueswir1 已提交
1450
#endif
1451 1452
    /* timers */
    fdctrl->fifo[4] = fdctrl->timer0;
B
blueswir1 已提交
1453
    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1454 1455 1456 1457 1458 1459 1460 1461
    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;
    fdctrl_set_fifo(fdctrl, 10, 0);
}

B
Blue Swirl 已提交
1462
static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
1463 1464 1465
{
    /* Controller's version */
    fdctrl->fifo[0] = fdctrl->version;
1466
    fdctrl_set_fifo(fdctrl, 1, 0);
1467 1468
}

B
Blue Swirl 已提交
1469
static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
1470 1471 1472 1473 1474
{
    fdctrl->fifo[0] = 0x41; /* Stepping 1 */
    fdctrl_set_fifo(fdctrl, 1, 0);
}

B
Blue Swirl 已提交
1475
static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
1476
{
B
Blue Swirl 已提交
1477
    FDrive *cur_drv = get_cur_drv(fdctrl);
1478 1479 1480 1481

    /* Drives position */
    drv0(fdctrl)->track = fdctrl->fifo[3];
    drv1(fdctrl)->track = fdctrl->fifo[4];
B
blueswir1 已提交
1482 1483 1484 1485
#if MAX_FD == 4
    drv2(fdctrl)->track = fdctrl->fifo[5];
    drv3(fdctrl)->track = fdctrl->fifo[6];
#endif
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497
    /* 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 已提交
1498
static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
1499
{
B
Blue Swirl 已提交
1500
    FDrive *cur_drv = get_cur_drv(fdctrl);
1501 1502 1503 1504 1505 1506

    fdctrl->fifo[0] = 0;
    fdctrl->fifo[1] = 0;
    /* Drives position */
    fdctrl->fifo[2] = drv0(fdctrl)->track;
    fdctrl->fifo[3] = drv1(fdctrl)->track;
B
blueswir1 已提交
1507 1508 1509 1510
#if MAX_FD == 4
    fdctrl->fifo[4] = drv2(fdctrl)->track;
    fdctrl->fifo[5] = drv3(fdctrl)->track;
#else
1511 1512
    fdctrl->fifo[4] = 0;
    fdctrl->fifo[5] = 0;
B
blueswir1 已提交
1513
#endif
1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
    /* 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;
1525
    fdctrl_set_fifo(fdctrl, 15, 0);
1526 1527
}

B
Blue Swirl 已提交
1528
static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
1529
{
B
Blue Swirl 已提交
1530
    FDrive *cur_drv = get_cur_drv(fdctrl);
1531 1532 1533

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

B
Blue Swirl 已提交
1537
static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
1538
{
B
Blue Swirl 已提交
1539
    FDrive *cur_drv;
1540

B
blueswir1 已提交
1541
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
    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;
    fdctrl->data_state &= ~FD_STATE_SEEK;
    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 已提交
1566
static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
1567 1568 1569
{
    fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
    fdctrl->timer1 = fdctrl->fifo[2] >> 1;
B
blueswir1 已提交
1570 1571 1572 1573
    if (fdctrl->fifo[2] & 1)
        fdctrl->dor &= ~FD_DOR_DMAEN;
    else
        fdctrl->dor |= FD_DOR_DMAEN;
1574 1575 1576 1577
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1578
static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
1579
{
B
Blue Swirl 已提交
1580
    FDrive *cur_drv;
1581

B
blueswir1 已提交
1582
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1583 1584 1585 1586 1587 1588
    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 已提交
1589
        GET_CUR_DRV(fdctrl) |
1590 1591 1592 1593
        0x28;
    fdctrl_set_fifo(fdctrl, 1, 0);
}

B
Blue Swirl 已提交
1594
static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
1595
{
B
Blue Swirl 已提交
1596
    FDrive *cur_drv;
1597

B
blueswir1 已提交
1598
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1599 1600 1601 1602 1603 1604 1605
    cur_drv = get_cur_drv(fdctrl);
    fd_recalibrate(cur_drv);
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

B
Blue Swirl 已提交
1606
static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
1607
{
B
Blue Swirl 已提交
1608
    FDrive *cur_drv = get_cur_drv(fdctrl);
1609

P
Pavel Hrdina 已提交
1610
    if (fdctrl->reset_sensei > 0) {
1611 1612 1613
        fdctrl->fifo[0] =
            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
        fdctrl->reset_sensei--;
P
Pavel Hrdina 已提交
1614 1615 1616 1617
    } else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
        fdctrl->fifo[0] = FD_SR0_INVCMD;
        fdctrl_set_fifo(fdctrl, 1, 0);
        return;
1618 1619
    } else {
        fdctrl->fifo[0] =
P
Pavel Hrdina 已提交
1620 1621
                (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0))
                | GET_CUR_DRV(fdctrl);
1622 1623
    }

1624 1625 1626
    fdctrl->fifo[1] = cur_drv->track;
    fdctrl_set_fifo(fdctrl, 2, 0);
    fdctrl_reset_irq(fdctrl);
B
blueswir1 已提交
1627
    fdctrl->status0 = FD_SR0_RDYCHG;
1628 1629
}

B
Blue Swirl 已提交
1630
static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
1631
{
B
Blue Swirl 已提交
1632
    FDrive *cur_drv;
1633

B
blueswir1 已提交
1634
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1635 1636
    cur_drv = get_cur_drv(fdctrl);
    fdctrl_reset_fifo(fdctrl);
1637 1638 1639
    /* 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.
     */
1640
    fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
1641 1642
    /* Raise Interrupt */
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1643 1644
}

B
Blue Swirl 已提交
1645
static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
1646
{
B
Blue Swirl 已提交
1647
    FDrive *cur_drv = get_cur_drv(fdctrl);
1648 1649 1650 1651

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

B
Blue Swirl 已提交
1655
static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
1656 1657 1658 1659 1660 1661 1662
{
    fdctrl->config = fdctrl->fifo[2];
    fdctrl->precomp_trk =  fdctrl->fifo[3];
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1663
static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
1664 1665 1666
{
    fdctrl->pwrd = fdctrl->fifo[1];
    fdctrl->fifo[0] = fdctrl->fifo[1];
1667
    fdctrl_set_fifo(fdctrl, 1, 0);
1668 1669
}

B
Blue Swirl 已提交
1670
static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
1671 1672 1673 1674 1675
{
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

B
Blue Swirl 已提交
1676
static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
1677
{
B
Blue Swirl 已提交
1678
    FDrive *cur_drv = get_cur_drv(fdctrl);
1679 1680 1681 1682 1683 1684 1685

    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;
1686
            fdctrl_set_fifo(fdctrl, 4, 0);
1687 1688 1689 1690 1691 1692
        } else {
            fdctrl_reset_fifo(fdctrl);
        }
    } else if (fdctrl->data_len > 7) {
        /* ERROR */
        fdctrl->fifo[0] = 0x80 |
B
blueswir1 已提交
1693
            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1694
        fdctrl_set_fifo(fdctrl, 1, 0);
1695 1696 1697
    }
}

B
Blue Swirl 已提交
1698
static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
1699
{
B
Blue Swirl 已提交
1700
    FDrive *cur_drv;
1701

B
blueswir1 已提交
1702
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1703 1704
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
1705 1706
        fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1,
                cur_drv->sect, 1);
1707
    } else {
1708
        fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
1709 1710
    }
    fdctrl_reset_fifo(fdctrl);
B
blueswir1 已提交
1711
    /* Raise Interrupt */
1712 1713 1714
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

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

B
blueswir1 已提交
1719
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1720 1721
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->fifo[2] > cur_drv->track) {
1722
        fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1);
1723
    } else {
1724
        fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
1725 1726 1727 1728 1729 1730
    }
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

B
blueswir1 已提交
1731 1732 1733 1734 1735
static const struct {
    uint8_t value;
    uint8_t mask;
    const char* name;
    int parameters;
B
Blue Swirl 已提交
1736
    void (*handler)(FDCtrl *fdctrl, int direction);
B
blueswir1 已提交
1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774
    int direction;
} handlers[] = {
    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
};
/* Associate command to an index in the 'handlers' array */
static uint8_t command_to_handler[256];

B
Blue Swirl 已提交
1775
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
1776
{
B
Blue Swirl 已提交
1777
    FDrive *cur_drv;
1778
    int pos;
1779

B
bellard 已提交
1780
    /* Reset mode */
B
blueswir1 已提交
1781
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
B
bellard 已提交
1782
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
1783 1784
        return;
    }
B
blueswir1 已提交
1785
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
B
Blue Swirl 已提交
1786
        FLOPPY_DPRINTF("error: controller not ready for writing\n");
B
bellard 已提交
1787 1788
        return;
    }
B
blueswir1 已提交
1789
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
B
bellard 已提交
1790
    /* Is it write command time ? */
B
blueswir1 已提交
1791
    if (fdctrl->msr & FD_MSR_NONDMA) {
B
bellard 已提交
1792
        /* FIFO data write */
1793 1794 1795 1796
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
1797
            fdctrl->data_pos == fdctrl->data_len) {
B
blueswir1 已提交
1798 1799
            cur_drv = get_cur_drv(fdctrl);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
B
Blue Swirl 已提交
1800 1801
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
B
blueswir1 已提交
1802 1803
                return;
            }
B
blueswir1 已提交
1804 1805 1806 1807 1808
            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 已提交
1809
        }
1810
        /* Switch from transfer mode to status mode
B
bellard 已提交
1811 1812
         * then from status mode to command mode
         */
B
blueswir1 已提交
1813
        if (fdctrl->data_pos == fdctrl->data_len)
1814
            fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
B
bellard 已提交
1815 1816
        return;
    }
1817
    if (fdctrl->data_pos == 0) {
B
bellard 已提交
1818
        /* Command */
B
blueswir1 已提交
1819 1820 1821
        pos = command_to_handler[value & 0xff];
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        fdctrl->data_len = handlers[pos].parameters + 1;
1822
        fdctrl->msr |= FD_MSR_CMDBUSY;
B
bellard 已提交
1823
    }
B
blueswir1 已提交
1824

1825
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
B
blueswir1 已提交
1826 1827
    fdctrl->fifo[fdctrl->data_pos++] = value;
    if (fdctrl->data_pos == fdctrl->data_len) {
B
bellard 已提交
1828 1829 1830
        /* We now have all parameters
         * and will be able to treat the command
         */
1831 1832
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            fdctrl_format_sector(fdctrl);
B
bellard 已提交
1833 1834
            return;
        }
1835

B
blueswir1 已提交
1836 1837 1838
        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 已提交
1839 1840
    }
}
1841 1842 1843

static void fdctrl_result_timer(void *opaque)
{
B
Blue Swirl 已提交
1844 1845
    FDCtrl *fdctrl = opaque;
    FDrive *cur_drv = get_cur_drv(fdctrl);
1846

1847 1848 1849 1850 1851 1852 1853
    /* 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;
    }
1854 1855 1856 1857 1858 1859 1860 1861 1862
    /* 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);
    }
1863
}
B
blueswir1 已提交
1864

1865
static void fdctrl_change_cb(void *opaque, bool load)
1866 1867 1868 1869
{
    FDrive *drive = opaque;

    drive->media_changed = 1;
1870
    fd_revalidate(drive);
1871 1872 1873 1874 1875 1876
}

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

B
blueswir1 已提交
1877
/* Init functions */
1878
static int fdctrl_connect_drives(FDCtrl *fdctrl)
B
blueswir1 已提交
1879
{
B
Blue Swirl 已提交
1880
    unsigned int i;
1881
    FDrive *drive;
B
blueswir1 已提交
1882 1883

    for (i = 0; i < MAX_FD; i++) {
1884
        drive = &fdctrl->drives[i];
1885
        drive->fdctrl = fdctrl;
1886

1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897
        if (drive->bs) {
            if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
                error_report("fdc doesn't support drive option werror");
                return -1;
            }
            if (bdrv_get_on_error(drive->bs, 1) != BLOCK_ERR_REPORT) {
                error_report("fdc doesn't support drive option rerror");
                return -1;
            }
        }

1898
        fd_init(drive);
P
Pavel Hrdina 已提交
1899
        fdctrl_change_cb(drive, 0);
1900
        if (drive->bs) {
1901
            bdrv_set_dev_ops(drive->bs, &fdctrl_block_ops, drive);
1902
        }
B
blueswir1 已提交
1903
    }
1904
    return 0;
B
blueswir1 已提交
1905 1906
}

M
Markus Armbruster 已提交
1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926
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 已提交
1927 1928
void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
                        target_phys_addr_t mmio_base, DriveInfo **fds)
G
Gerd Hoffmann 已提交
1929
{
B
Blue Swirl 已提交
1930
    FDCtrl *fdctrl;
G
Gerd Hoffmann 已提交
1931
    DeviceState *dev;
B
Blue Swirl 已提交
1932
    FDCtrlSysBus *sys;
G
Gerd Hoffmann 已提交
1933 1934

    dev = qdev_create(NULL, "sysbus-fdc");
B
Blue Swirl 已提交
1935
    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
1936 1937
    fdctrl = &sys->state;
    fdctrl->dma_chann = dma_chann; /* FIXME */
1938
    if (fds[0]) {
1939
        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
1940 1941
    }
    if (fds[1]) {
1942
        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
1943
    }
M
Markus Armbruster 已提交
1944
    qdev_init_nofail(dev);
G
Gerd Hoffmann 已提交
1945 1946
    sysbus_connect_irq(&sys->busdev, 0, irq);
    sysbus_mmio_map(&sys->busdev, 0, mmio_base);
B
blueswir1 已提交
1947 1948
}

B
Blue Swirl 已提交
1949 1950
void sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base,
                       DriveInfo **fds, qemu_irq *fdc_tc)
B
blueswir1 已提交
1951
{
B
Blue Swirl 已提交
1952
    DeviceState *dev;
B
Blue Swirl 已提交
1953
    FDCtrlSysBus *sys;
B
blueswir1 已提交
1954

B
Blue Swirl 已提交
1955
    dev = qdev_create(NULL, "SUNW,fdtwo");
1956
    if (fds[0]) {
1957
        qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
1958
    }
M
Markus Armbruster 已提交
1959
    qdev_init_nofail(dev);
B
Blue Swirl 已提交
1960
    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
G
Gerd Hoffmann 已提交
1961 1962
    sysbus_connect_irq(&sys->busdev, 0, irq);
    sysbus_mmio_map(&sys->busdev, 0, io_base);
B
Blue Swirl 已提交
1963
    *fdc_tc = qdev_get_gpio_in(dev, 0);
B
blueswir1 已提交
1964
}
B
Blue Swirl 已提交
1965

J
Jan Kiszka 已提交
1966
static int fdctrl_init_common(FDCtrl *fdctrl)
B
Blue Swirl 已提交
1967
{
B
Blue Swirl 已提交
1968 1969
    int i, j;
    static int command_tables_inited = 0;
B
Blue Swirl 已提交
1970

B
Blue Swirl 已提交
1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984
    /* 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 已提交
1985
    fdctrl->fifo_size = 512;
1986
    fdctrl->result_timer = qemu_new_timer_ns(vm_clock,
B
Blue Swirl 已提交
1987 1988 1989 1990
                                          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 已提交
1991
    fdctrl->num_floppies = MAX_FD;
B
Blue Swirl 已提交
1992

1993 1994
    if (fdctrl->dma_chann != -1)
        DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
1995
    return fdctrl_connect_drives(fdctrl);
B
Blue Swirl 已提交
1996 1997
}

1998
static const MemoryRegionPortio fdc_portio_list[] = {
1999
    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
2000 2001
    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
    PORTIO_END_OF_LIST(),
2002 2003
};

2004
static int isabus_fdc_init1(ISADevice *dev)
G
Gerd Hoffmann 已提交
2005
{
B
Blue Swirl 已提交
2006 2007
    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
    FDCtrl *fdctrl = &isa->state;
B
Blue Swirl 已提交
2008
    int ret;
G
Gerd Hoffmann 已提交
2009

2010
    isa_register_portio_list(dev, isa->iobase, fdc_portio_list, fdctrl, "fdc");
2011

2012 2013
    isa_init_irq(&isa->busdev, &fdctrl->irq, isa->irq);
    fdctrl->dma_chann = isa->dma;
G
Gerd Hoffmann 已提交
2014

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

2018 2019 2020
    add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0");
    add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1");

B
Blue Swirl 已提交
2021
    return ret;
G
Gerd Hoffmann 已提交
2022 2023
}

2024
static int sysbus_fdc_init1(SysBusDevice *dev)
B
Blue Swirl 已提交
2025
{
B
Blue Swirl 已提交
2026 2027
    FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev);
    FDCtrl *fdctrl = &sys->state;
B
Blue Swirl 已提交
2028
    int ret;
B
Blue Swirl 已提交
2029

A
Avi Kivity 已提交
2030
    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_ops, fdctrl, "fdc", 0x08);
2031
    sysbus_init_mmio(dev, &fdctrl->iomem);
G
Gerd Hoffmann 已提交
2032 2033
    sysbus_init_irq(dev, &fdctrl->irq);
    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
2034
    fdctrl->dma_chann = -1;
G
Gerd Hoffmann 已提交
2035

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

    return ret;
B
Blue Swirl 已提交
2040 2041
}

2042
static int sun4m_fdc_init1(SysBusDevice *dev)
B
Blue Swirl 已提交
2043
{
B
Blue Swirl 已提交
2044
    FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state);
B
Blue Swirl 已提交
2045

A
Avi Kivity 已提交
2046 2047
    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_strict_ops, fdctrl,
                          "fdctrl", 0x08);
2048
    sysbus_init_mmio(dev, &fdctrl->iomem);
G
Gerd Hoffmann 已提交
2049 2050 2051 2052
    sysbus_init_irq(dev, &fdctrl->irq);
    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);

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

K
Kevin Wolf 已提交
2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
void fdc_get_bs(BlockDriverState *bs[], ISADevice *dev)
{
    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
    FDCtrl *fdctrl = &isa->state;
    int i;

    for (i = 0; i < MAX_FD; i++) {
        bs[i] = fdctrl->drives[i].bs;
    }
}


J
Jan Kiszka 已提交
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078
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()
    }
};

2079
static Property isa_fdc_properties[] = {
2080 2081 2082
    DEFINE_PROP_HEX32("iobase", FDCtrlISABus, iobase, 0x3f0),
    DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
    DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
2083 2084 2085 2086
    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),
2087 2088
    DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
                    0, true),
2089 2090 2091
    DEFINE_PROP_END_OF_LIST(),
};

2092 2093
static void isabus_fdc_class_init1(ObjectClass *klass, void *data)
{
2094
    DeviceClass *dc = DEVICE_CLASS(klass);
2095 2096
    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
    ic->init = isabus_fdc_init1;
2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108
    dc->fw_name = "fdc";
    dc->no_user = 1;
    dc->reset = fdctrl_external_reset_isa;
    dc->vmsd = &vmstate_isa_fdc;
    dc->props = isa_fdc_properties;
}

static TypeInfo isa_fdc_info = {
    .name          = "isa-fdc",
    .parent        = TYPE_ISA_DEVICE,
    .instance_size = sizeof(FDCtrlISABus),
    .class_init    = isabus_fdc_class_init1,
G
Gerd Hoffmann 已提交
2109 2110
};

J
Jan Kiszka 已提交
2111 2112 2113 2114 2115 2116 2117 2118 2119 2120
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()
    }
};

2121 2122 2123 2124
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 已提交
2125 2126
};

2127 2128
static void sysbus_fdc_class_init(ObjectClass *klass, void *data)
{
2129
    DeviceClass *dc = DEVICE_CLASS(klass);
2130 2131 2132
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = sysbus_fdc_init1;
2133 2134 2135
    dc->reset = fdctrl_external_reset_sysbus;
    dc->vmsd = &vmstate_sysbus_fdc;
    dc->props = sysbus_fdc_properties;
2136 2137
}

2138 2139 2140 2141 2142
static TypeInfo sysbus_fdc_info = {
    .name          = "sysbus-fdc",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(FDCtrlSysBus),
    .class_init    = sysbus_fdc_class_init,
2143 2144 2145 2146 2147 2148 2149 2150 2151
};

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)
{
2152
    DeviceClass *dc = DEVICE_CLASS(klass);
2153 2154 2155
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = sun4m_fdc_init1;
2156 2157 2158
    dc->reset = fdctrl_external_reset_sysbus;
    dc->vmsd = &vmstate_sysbus_fdc;
    dc->props = sun4m_fdc_properties;
2159 2160
}

2161 2162 2163 2164 2165
static TypeInfo sun4m_fdc_info = {
    .name          = "SUNW,fdtwo",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(FDCtrlSysBus),
    .class_init    = sun4m_fdc_class_init,
B
Blue Swirl 已提交
2166 2167
};

A
Andreas Färber 已提交
2168
static void fdc_register_types(void)
B
Blue Swirl 已提交
2169
{
2170 2171 2172
    type_register_static(&isa_fdc_info);
    type_register_static(&sysbus_fdc_info);
    type_register_static(&sun4m_fdc_info);
B
Blue Swirl 已提交
2173 2174
}

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