fdc.c 55.0 KB
Newer Older
B
bellard 已提交
1
/*
2
 * QEMU Floppy disk emulator (Intel 82078)
B
bellard 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
 * 
 * Copyright (c) 2003 Jocelyn Mayer
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "vl.h"

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

#ifdef DEBUG_FLOPPY
#define FLOPPY_DPRINTF(fmt, args...) \
do { printf("FLOPPY: " fmt , ##args); } while (0)
#else
#define FLOPPY_DPRINTF(fmt, args...)
#endif

#define FLOPPY_ERROR(fmt, args...) \
do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)

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

/* Will always be a fixed parameter for us */
#define FD_SECTOR_LEN 512
#define FD_SECTOR_SC  2   /* Sector size code */

/* Floppy disk drive emulation */
typedef enum fdisk_type_t {
    FDRIVE_DISK_288   = 0x01, /* 2.88 MB disk           */
    FDRIVE_DISK_144   = 0x02, /* 1.44 MB disk           */
    FDRIVE_DISK_720   = 0x03, /* 720 kB disk            */
52 53
    FDRIVE_DISK_USER  = 0x04, /* User defined geometry  */
    FDRIVE_DISK_NONE  = 0x05, /* No disk                */
B
bellard 已提交
54 55 56 57 58 59 60 61 62
} fdisk_type_t;

typedef enum fdrive_type_t {
    FDRIVE_DRV_144  = 0x00,   /* 1.44 MB 3"5 drive      */
    FDRIVE_DRV_288  = 0x01,   /* 2.88 MB 3"5 drive      */
    FDRIVE_DRV_120  = 0x02,   /* 1.2  MB 5"25 drive     */
    FDRIVE_DRV_NONE = 0x03,   /* No drive connected     */
} fdrive_type_t;

63 64 65 66 67 68 69 70 71
typedef enum fdrive_flags_t {
    FDRIVE_MOTOR_ON   = 0x01, /* motor on/off           */
    FDRIVE_REVALIDATE = 0x02, /* Revalidated            */
} fdrive_flags_t;

typedef enum fdisk_flags_t {
    FDISK_DBL_SIDES  = 0x01,
} fdisk_flags_t;

B
bellard 已提交
72 73 74 75
typedef struct fdrive_t {
    BlockDriverState *bs;
    /* Drive status */
    fdrive_type_t drive;
76
    fdrive_flags_t drflags;
B
bellard 已提交
77 78 79 80 81 82 83 84 85
    uint8_t perpendicular;    /* 2.88 MB access mode    */
    /* Position */
    uint8_t head;
    uint8_t track;
    uint8_t sect;
    /* Last operation status */
    uint8_t dir;              /* Direction              */
    uint8_t rw;               /* Read/write             */
    /* Media */
86
    fdisk_flags_t flags;
B
bellard 已提交
87 88
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
89
    uint16_t bps;             /* Bytes per sector       */
B
bellard 已提交
90 91 92
    uint8_t ro;               /* Is read-only           */
} fdrive_t;

93
static void fd_init (fdrive_t *drv, BlockDriverState *bs)
B
bellard 已提交
94 95
{
    /* Drive */
96
    drv->bs = bs;
B
bellard 已提交
97
    drv->drive = FDRIVE_DRV_NONE;
98
    drv->drflags = 0;
B
bellard 已提交
99 100
    drv->perpendicular = 0;
    /* Disk */
101
    drv->last_sect = 0;
B
bellard 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
    drv->max_track = 0;
}

static int _fd_sector (uint8_t head, uint8_t track,
                        uint8_t sect, uint8_t last_sect)
{
    return (((track * 2) + head) * last_sect) + sect - 1;
}

/* Returns current position, in sectors, for given drive */
static int fd_sector (fdrive_t *drv)
{
    return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);
}

static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
                    int enable_seek)
{
    uint32_t sector;
121 122 123 124
    int ret;

    if (track > drv->max_track ||
	(head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
125 126 127 128
        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 已提交
129 130 131
        return 2;
    }
    if (sect > drv->last_sect) {
132 133 134 135
        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 已提交
136 137 138
        return 3;
    }
    sector = _fd_sector(head, track, sect, drv->last_sect);
139
    ret = 0;
B
bellard 已提交
140 141 142 143 144 145 146 147 148
    if (sector != fd_sector(drv)) {
#if 0
        if (!enable_seek) {
            FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
                         head, track, sect, 1, drv->max_track, drv->last_sect);
            return 4;
        }
#endif
        drv->head = head;
149 150
	if (drv->track != track)
	    ret = 1;
B
bellard 已提交
151 152 153 154
        drv->track = track;
        drv->sect = sect;
    }

155
    return ret;
B
bellard 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168
}

/* Set drive back to track 0 */
static void fd_recalibrate (fdrive_t *drv)
{
    FLOPPY_DPRINTF("recalibrate\n");
    drv->head = 0;
    drv->track = 0;
    drv->sect = 1;
    drv->dir = 1;
    drv->rw = 0;
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
/* Recognize floppy formats */
typedef struct fd_format_t {
    fdrive_type_t drive;
    fdisk_type_t  disk;
    uint8_t last_sect;
    uint8_t max_track;
    uint8_t max_head;
    const unsigned char *str;
} fd_format_t;

static fd_format_t fd_formats[] = {
    /* First entry is default format */
    /* 1.44 MB 3"1/2 floppy disks */
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1,  "1.6 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
    /* 2.88 MB 3"1/2 floppy disks */
    { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
    { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
    { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1,  "3.2 MB 3\"1/2", },
    { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
    { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
    /* 720 kB 3"1/2 floppy disks */
    { FDRIVE_DRV_144, FDRIVE_DISK_720,  9, 80, 1,  "720 kB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1,  "800 kB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1,  "820 kB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1,  "830 kB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
    { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
    /* 1.2 MB 5"1/4 floppy disks */
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1,  "1.2 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1,  "1.6 MB 5\"1/4", },
    /* 720 kB 5"1/4 floppy disks */
    { FDRIVE_DRV_120, FDRIVE_DISK_288,  9, 80, 1,  "720 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1,  "880 kB 5\"1/4", },
    /* 360 kB 5"1/4 floppy disks */
    { FDRIVE_DRV_120, FDRIVE_DISK_288,  9, 40, 1,  "360 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288,  9, 40, 0,  "180 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1,  "410 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1,  "420 kB 5\"1/4", },
    /* 320 kB 5"1/4 floppy disks */ 
    { FDRIVE_DRV_120, FDRIVE_DISK_288,  8, 40, 1,  "320 kB 5\"1/4", },
    { FDRIVE_DRV_120, FDRIVE_DISK_288,  8, 40, 0,  "160 kB 5\"1/4", },
    /* 360 kB must match 5"1/4 better than 3"1/2... */
    { FDRIVE_DRV_144, FDRIVE_DISK_720,  9, 80, 0,  "360 kB 3\"1/2", },
    /* end */
    { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
};

B
bellard 已提交
226
/* Revalidate a disk drive after a disk change */
227
static void fd_revalidate (fdrive_t *drv)
B
bellard 已提交
228
{
229 230 231
    fd_format_t *parse;
    int64_t nb_sectors, size;
    int i, first_match, match;
232
    int nb_heads, max_track, last_sect, ro;
B
bellard 已提交
233 234

    FLOPPY_DPRINTF("revalidate\n");
235
    drv->drflags &= ~FDRIVE_REVALIDATE;
236
    if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
237
	ro = bdrv_is_read_only(drv->bs);
238
	bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
239
	if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
240 241
	    FLOPPY_DPRINTF("User defined disk (%d %d %d)",
                           nb_heads - 1, max_track, last_sect);
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	} else {
	    bdrv_get_geometry(drv->bs, &nb_sectors);
	    match = -1;
	    first_match = -1;
	    for (i = 0;; i++) {
		parse = &fd_formats[i];
		if (parse->drive == FDRIVE_DRV_NONE)
		    break;
		if (drv->drive == parse->drive ||
		    drv->drive == FDRIVE_DRV_NONE) {
		    size = (parse->max_head + 1) * parse->max_track *
			parse->last_sect;
		    if (nb_sectors == size) {
			match = i;
			break;
		    }
		    if (first_match == -1)
			first_match = i;
		}
	    }
	    if (match == -1) {
		if (first_match == -1)
		    match = 1;
		else
		    match = first_match;
		parse = &fd_formats[match];
	    }
	    nb_heads = parse->max_head + 1;
	    max_track = parse->max_track;
	    last_sect = parse->last_sect;
	    drv->drive = parse->drive;
273 274
	    FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
                           nb_heads, max_track, last_sect, ro ? "ro" : "rw");
275
	}
276 277 278 279 280 281 282 283
	    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;
B
bellard 已提交
284
    } else {
285
	FLOPPY_DPRINTF("No disk in drive\n");
286 287 288
        drv->last_sect = 0;
	drv->max_track = 0;
	drv->flags &= ~FDISK_DBL_SIDES;
B
bellard 已提交
289
    }
290
    drv->drflags |= FDRIVE_REVALIDATE;
291 292
}

B
bellard 已提交
293 294 295
/* Motor control */
static void fd_start (fdrive_t *drv)
{
296
    drv->drflags |= FDRIVE_MOTOR_ON;
B
bellard 已提交
297 298 299 300
}

static void fd_stop (fdrive_t *drv)
{
301
    drv->drflags &= ~FDRIVE_MOTOR_ON;
B
bellard 已提交
302 303 304 305 306 307 308 309 310 311
}

/* Re-initialise a drives (motor off, repositioned) */
static void fd_reset (fdrive_t *drv)
{
    fd_stop(drv);
    fd_recalibrate(drv);
}

/********************************************************/
B
bellard 已提交
312
/* Intel 82078 floppy disk controller emulation          */
B
bellard 已提交
313

314 315
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
316
static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size);
317
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
318
static void fdctrl_result_timer(void *opaque);
319 320 321 322 323 324 325 326 327 328 329

static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl);
static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl);
static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value);
static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl);
static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value);
static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl);
static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value);
static uint32_t fdctrl_read_data (fdctrl_t *fdctrl);
static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value);
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl);
B
bellard 已提交
330 331

enum {
332
    FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */
B
bellard 已提交
333
    FD_CTRL_RESET  = 0x02,
334 335
    FD_CTRL_SLEEP  = 0x04, /* XXX: suppress that */
    FD_CTRL_BUSY   = 0x08, /* dma transfer in progress */
B
bellard 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
    FD_CTRL_INTR   = 0x10,
};

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

enum {
    FD_STATE_CMD    = 0x00,
    FD_STATE_STATUS = 0x01,
    FD_STATE_DATA   = 0x02,
    FD_STATE_STATE  = 0x03,
    FD_STATE_MULTI  = 0x10,
    FD_STATE_SEEK   = 0x20,
354
    FD_STATE_FORMAT = 0x40,
B
bellard 已提交
355 356 357
};

#define FD_STATE(state) ((state) & FD_STATE_STATE)
358 359
#define FD_SET_STATE(state, new_state) \
do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)
B
bellard 已提交
360 361
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
362
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
B
bellard 已提交
363

364 365
struct fdctrl_t {
    fdctrl_t *fdctrl;
B
bellard 已提交
366
    /* Controller's identification */
B
bellard 已提交
367 368 369 370
    uint8_t version;
    /* HW */
    int irq_lvl;
    int dma_chann;
371
    uint32_t io_base;
B
bellard 已提交
372
    /* Controller state */
373
    QEMUTimer *result_timer;
B
bellard 已提交
374 375 376 377 378 379 380 381 382 383 384
    uint8_t state;
    uint8_t dma_en;
    uint8_t cur_drv;
    uint8_t bootsel;
    /* Command FIFO */
    uint8_t fifo[FD_SECTOR_LEN];
    uint32_t data_pos;
    uint32_t data_len;
    uint8_t data_state;
    uint8_t data_dir;
    uint8_t int_status;
385
    uint8_t eot; /* last wanted sector */
B
bellard 已提交
386 387 388 389 390 391 392 393 394 395 396 397
    /* States kept only to be returned back */
    /* Timers state */
    uint8_t timer0;
    uint8_t timer1;
    /* precompensation */
    uint8_t precomp_trk;
    uint8_t config;
    uint8_t lock;
    /* Power down config (also with status regB access mode */
    uint8_t pwrd;
    /* Floppy drives */
    fdrive_t drives[2];
398 399 400 401 402 403 404
};

static uint32_t fdctrl_read (void *opaque, uint32_t reg)
{
    fdctrl_t *fdctrl = opaque;
    uint32_t retval;

405 406
    switch (reg & 0x07) {
    case 0x01:
407
	retval = fdctrl_read_statusB(fdctrl);
408 409
	break;
    case 0x02:
410
	retval = fdctrl_read_dor(fdctrl);
411 412
	break;
    case 0x03:
413
        retval = fdctrl_read_tape(fdctrl);
414 415
	break;
    case 0x04:
416
        retval = fdctrl_read_main_status(fdctrl);
417 418
	break;
    case 0x05:
419
        retval = fdctrl_read_data(fdctrl);
420 421
	break;
    case 0x07:
422
        retval = fdctrl_read_dir(fdctrl);
423 424
	break;
    default:
425
	retval = (uint32_t)(-1);
426 427
	break;
    }
428
    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
429 430 431 432 433 434 435 436

    return retval;
}

static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    fdctrl_t *fdctrl = opaque;

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

439 440
    switch (reg & 0x07) {
    case 0x02:
441
	fdctrl_write_dor(fdctrl, value);
442 443
	break;
    case 0x03:
444
        fdctrl_write_tape(fdctrl, value);
445 446
	break;
    case 0x04:
447
        fdctrl_write_rate(fdctrl, value);
448 449
	break;
    case 0x05:
450
        fdctrl_write_data(fdctrl, value);
451 452 453 454
	break;
    default:
	break;
    }
455 456 457 458 459
}

static void fd_change_cb (void *opaque)
{
    fdrive_t *drv = opaque;
B
bellard 已提交
460

461 462 463 464 465 466 467 468
    FLOPPY_DPRINTF("disk change\n");
    fd_revalidate(drv);
#if 0
    fd_recalibrate(drv);
    fdctrl_reset_fifo(drv->fdctrl);
    fdctrl_raise_irq(drv->fdctrl, 0x20);
#endif
}
B
bellard 已提交
469

470 471 472
fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, 
                       uint32_t io_base,
                       BlockDriverState **fds)
B
bellard 已提交
473
{
474
    fdctrl_t *fdctrl;
B
bellard 已提交
475 476 477
//    int io_mem;
    int i;

B
bellard 已提交
478
    FLOPPY_DPRINTF("init controller\n");
479 480 481
    fdctrl = qemu_mallocz(sizeof(fdctrl_t));
    if (!fdctrl)
        return NULL;
482 483 484
    fdctrl->result_timer = qemu_new_timer(vm_clock, 
                                          fdctrl_result_timer, fdctrl);

B
bellard 已提交
485
    fdctrl->version = 0x90; /* Intel 82078 controller */
486 487 488
    fdctrl->irq_lvl = irq_lvl;
    fdctrl->dma_chann = dma_chann;
    fdctrl->io_base = io_base;
489
    fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
490 491 492
    if (fdctrl->dma_chann != -1) {
        fdctrl->dma_en = 1;
        DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
B
bellard 已提交
493
    } else {
494
        fdctrl->dma_en = 0;
B
bellard 已提交
495
    }
496 497 498 499 500 501
    for (i = 0; i < 2; i++) {
        fd_init(&fdctrl->drives[i], fds[i]);
        if (fds[i]) {
            bdrv_set_change_cb(fds[i],
                               &fd_change_cb, &fdctrl->drives[i]);
        }
502
    }
503 504
    fdctrl_reset(fdctrl, 0);
    fdctrl->state = FD_CTRL_ACTIVE;
B
bellard 已提交
505 506 507 508 509 510 511
    if (mem_mapped) {
        FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
#if 0
        io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
        cpu_register_physical_memory(base, 0x08, io_mem);
#endif
    } else {
512 513 514 515
        register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl);
        register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl);
        register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl);
        register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl);
B
bellard 已提交
516
    }
517
    for (i = 0; i < 2; i++) {
518
        fd_revalidate(&fdctrl->drives[i]);
B
bellard 已提交
519
    }
520

521
    return fdctrl;
522
}
B
bellard 已提交
523

524 525
/* XXX: may change if moved to bdrv */
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
526
{
527
    return fdctrl->drives[drive_num].drive;
B
bellard 已提交
528 529 530
}

/* Change IRQ state */
531
static void fdctrl_reset_irq (fdctrl_t *fdctrl)
B
bellard 已提交
532
{
533 534 535
    FLOPPY_DPRINTF("Reset interrupt\n");
    pic_set_irq(fdctrl->irq_lvl, 0);
    fdctrl->state &= ~FD_CTRL_INTR;
B
bellard 已提交
536 537
}

538
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status)
B
bellard 已提交
539
{
540 541 542
    if (~(fdctrl->state & FD_CTRL_INTR)) {
        pic_set_irq(fdctrl->irq_lvl, 1);
        fdctrl->state |= FD_CTRL_INTR;
B
bellard 已提交
543 544
    }
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
545
    fdctrl->int_status = status;
B
bellard 已提交
546 547
}

B
bellard 已提交
548
/* Reset controller */
549
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
B
bellard 已提交
550 551 552
{
    int i;

B
bellard 已提交
553
    FLOPPY_DPRINTF("reset controller\n");
554
    fdctrl_reset_irq(fdctrl);
B
bellard 已提交
555
    /* Initialise controller */
556
    fdctrl->cur_drv = 0;
B
bellard 已提交
557
    /* FIFO state */
558 559 560 561
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
    fdctrl->data_state = FD_STATE_CMD;
    fdctrl->data_dir = FD_DIR_WRITE;
B
bellard 已提交
562
    for (i = 0; i < MAX_FD; i++)
563 564
        fd_reset(&fdctrl->drives[i]);
    fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
565
    if (do_irq)
566
        fdctrl_raise_irq(fdctrl, 0xc0);
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
}

static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
{
    return &fdctrl->drives[fdctrl->bootsel];
}

static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
{
    return &fdctrl->drives[1 - fdctrl->bootsel];
}

static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
{
    return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl);
B
bellard 已提交
582 583 584
}

/* Status B register : 0x01 (read-only) */
585
static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
B
bellard 已提交
586 587 588 589 590 591
{
    FLOPPY_DPRINTF("status register: 0x00\n");
    return 0;
}

/* Digital output register : 0x02 */
592
static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
B
bellard 已提交
593 594 595 596
{
    uint32_t retval = 0;

    /* Drive motors state indicators */
597 598 599 600
    if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
	retval |= 1 << 5;
    if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
	retval |= 1 << 4;
B
bellard 已提交
601
    /* DMA enable */
602
    retval |= fdctrl->dma_en << 3;
B
bellard 已提交
603
    /* Reset indicator */
604
    retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
B
bellard 已提交
605
    /* Selected drive */
606
    retval |= fdctrl->cur_drv;
B
bellard 已提交
607 608 609 610 611
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}

612
static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
613 614
{
    /* Reset mode */
615
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
616
        if (!(value & 0x04)) {
B
bellard 已提交
617
            FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
618 619 620 621 622 623
            return;
        }
    }
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
    /* Drive motors state indicators */
    if (value & 0x20)
624
        fd_start(drv1(fdctrl));
B
bellard 已提交
625
    else
626
        fd_stop(drv1(fdctrl));
B
bellard 已提交
627
    if (value & 0x10)
628
        fd_start(drv0(fdctrl));
B
bellard 已提交
629
    else
630
        fd_stop(drv0(fdctrl));
B
bellard 已提交
631 632
    /* DMA enable */
#if 0
633 634
    if (fdctrl->dma_chann != -1)
        fdctrl->dma_en = 1 - ((value >> 3) & 1);
B
bellard 已提交
635 636 637
#endif
    /* Reset */
    if (!(value & 0x04)) {
638
        if (!(fdctrl->state & FD_CTRL_RESET)) {
B
bellard 已提交
639
            FLOPPY_DPRINTF("controller enter RESET state\n");
640
            fdctrl->state |= FD_CTRL_RESET;
B
bellard 已提交
641 642
        }
    } else {
643
        if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
644
            FLOPPY_DPRINTF("controller out of RESET state\n");
645
            fdctrl_reset(fdctrl, 1);
646
            fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
B
bellard 已提交
647 648 649
        }
    }
    /* Selected drive */
650
    fdctrl->cur_drv = value & 1;
B
bellard 已提交
651 652 653
}

/* Tape drive register : 0x03 */
654
static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
B
bellard 已提交
655 656 657 658
{
    uint32_t retval = 0;

    /* Disk boot selection indicator */
659
    retval |= fdctrl->bootsel << 2;
B
bellard 已提交
660 661 662 663 664 665
    /* Tape indicators: never allowed */
    FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);

    return retval;
}

666
static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
667 668
{
    /* Reset mode */
669
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
670
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
671 672 673 674
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
675
    fdctrl->bootsel = (value >> 2) & 1;
B
bellard 已提交
676 677 678 679
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
680
static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
B
bellard 已提交
681 682 683
{
    uint32_t retval = 0;

684 685
    fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
    if (!(fdctrl->state & FD_CTRL_BUSY)) {
B
bellard 已提交
686 687 688
        /* Data transfer allowed */
        retval |= 0x80;
        /* Data transfer direction indicator */
689
        if (fdctrl->data_dir == FD_DIR_READ)
B
bellard 已提交
690 691 692 693
            retval |= 0x40;
    }
    /* Should handle 0x20 for SPECIFY command */
    /* Command busy indicator */
694 695
    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
        FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
B
bellard 已提交
696 697 698 699 700 701 702
        retval |= 0x10;
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
703
static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
704 705
{
    /* Reset mode */
706
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
707
            FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
708 709 710 711 712
            return;
        }
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
    if (value & 0x80) {
713 714 715
        fdctrl->state |= FD_CTRL_RESET;
        fdctrl_reset(fdctrl, 1);
        fdctrl->state &= ~FD_CTRL_RESET;
B
bellard 已提交
716 717
    }
    if (value & 0x40) {
718 719
        fdctrl->state |= FD_CTRL_SLEEP;
        fdctrl_reset(fdctrl, 1);
B
bellard 已提交
720 721 722 723 724
    }
//        fdctrl.precomp = (value >> 2) & 0x07;
}

/* Digital input register : 0x07 (read-only) */
725
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
B
bellard 已提交
726 727 728
{
    uint32_t retval = 0;

729 730
    if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
	drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
B
bellard 已提交
731 732
        retval |= 0x80;
    if (retval != 0)
733 734 735
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
    drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
    drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
B
bellard 已提交
736 737 738 739 740

    return retval;
}

/* FIFO state control */
741
static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
B
bellard 已提交
742
{
743 744 745
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
    FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD);
B
bellard 已提交
746 747 748
}

/* Set FIFO status for the host to read */
749
static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
B
bellard 已提交
750
{
751 752 753 754
    fdctrl->data_dir = FD_DIR_READ;
    fdctrl->data_len = fifo_len;
    fdctrl->data_pos = 0;
    FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS);
B
bellard 已提交
755
    if (do_irq)
756
        fdctrl_raise_irq(fdctrl, 0x00);
B
bellard 已提交
757 758 759
}

/* Set an error: unimplemented/unknown command */
760
static void fdctrl_unimplemented (fdctrl_t *fdctrl)
B
bellard 已提交
761 762
{
#if 0
763 764 765
    fdrive_t *cur_drv;

    cur_drv = get_cur_drv(fdctrl);
766
    fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv;
767 768 769
    fdctrl->fifo[1] = 0x00;
    fdctrl->fifo[2] = 0x00;
    fdctrl_set_fifo(fdctrl, 3, 1);
B
bellard 已提交
770
#else
771 772 773
    //    fdctrl_reset_fifo(fdctrl);
    fdctrl->fifo[0] = 0x80;
    fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
774 775 776 777
#endif
}

/* Callback for transfer end (stop or abort) */
778 779
static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
				  uint8_t status1, uint8_t status2)
B
bellard 已提交
780
{
781
    fdrive_t *cur_drv;
B
bellard 已提交
782

783
    cur_drv = get_cur_drv(fdctrl);
B
bellard 已提交
784 785
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
                   status0, status1, status2,
786 787
                   status0 | (cur_drv->head << 2) | fdctrl->cur_drv);
    fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv;
788 789 790 791 792 793 794
    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;
795
    if (fdctrl->state & FD_CTRL_BUSY) {
796
        DMA_release_DREQ(fdctrl->dma_chann);
797 798
        fdctrl->state &= ~FD_CTRL_BUSY;
    }
799
    fdctrl_set_fifo(fdctrl, 7, 1);
B
bellard 已提交
800 801 802
}

/* Prepare a data transfer (either DMA or FIFO) */
803
static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
B
bellard 已提交
804
{
805
    fdrive_t *cur_drv;
B
bellard 已提交
806 807 808
    uint8_t kh, kt, ks;
    int did_seek;

809 810 811 812 813
    fdctrl->cur_drv = fdctrl->fifo[1] & 1;
    cur_drv = get_cur_drv(fdctrl);
    kt = fdctrl->fifo[2];
    kh = fdctrl->fifo[3];
    ks = fdctrl->fifo[4];
B
bellard 已提交
814
    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
815
                   fdctrl->cur_drv, kh, kt, ks,
B
bellard 已提交
816 817
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
    did_seek = 0;
818
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
B
bellard 已提交
819 820
    case 2:
        /* sect too big */
821 822 823 824
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
825 826 827
        return;
    case 3:
        /* track too big */
828 829 830 831
        fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
832 833 834
        return;
    case 4:
        /* No seek enabled */
835 836 837 838
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
839 840 841 842 843 844 845 846
        return;
    case 1:
        did_seek = 1;
        break;
    default:
        break;
    }
    /* Set the FIFO state */
847 848 849 850 851 852 853
    fdctrl->data_dir = direction;
    fdctrl->data_pos = 0;
    FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
B
bellard 已提交
854
    if (did_seek)
855 856 857 858 859 860 861 862 863 864 865 866 867
        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 {
	int tmp;
        fdctrl->data_len = 128 << fdctrl->fifo[5];
        tmp = (cur_drv->last_sect - ks + 1);
        if (fdctrl->fifo[0] & 0x80)
            tmp += cur_drv->last_sect;
	fdctrl->data_len *= tmp;
    }
868
    fdctrl->eot = fdctrl->fifo[6];
869
    if (fdctrl->dma_en) {
B
bellard 已提交
870 871
        int dma_mode;
        /* DMA transfer are enabled. Check if DMA channel is well programmed */
872
        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
B
bellard 已提交
873
        dma_mode = (dma_mode >> 2) & 3;
874 875 876 877
        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
		       dma_mode, direction,
                       (128 << fdctrl->fifo[5]) *
		       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
B
bellard 已提交
878 879 880 881 882
        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 */
883
            fdctrl->state |= FD_CTRL_BUSY;
B
bellard 已提交
884
            /* Now, we just have to wait for the DMA controller to
B
bellard 已提交
885 886
             * recall us...
             */
887 888
            DMA_hold_DREQ(fdctrl->dma_chann);
            DMA_schedule(fdctrl->dma_chann);
B
bellard 已提交
889
            return;
890 891
        } else {
	    FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
B
bellard 已提交
892 893 894 895
        }
    }
    FLOPPY_DPRINTF("start non-DMA transfer\n");
    /* IO based transfer: calculate len */
896
    fdctrl_raise_irq(fdctrl, 0x00);
B
bellard 已提交
897 898 899 900 901

    return;
}

/* Prepare a transfer of deleted data */
902
static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
B
bellard 已提交
903 904 905 906
{
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
907
    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
B
bellard 已提交
908 909 910
}

/* handlers for DMA transfers */
911
static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
B
bellard 已提交
912
{
913 914 915
    fdctrl_t *fdctrl;
    fdrive_t *cur_drv;
    int len, start_pos, rel_pos;
B
bellard 已提交
916 917
    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;

918 919
    fdctrl = opaque;
    if (!(fdctrl->state & FD_CTRL_BUSY)) {
B
bellard 已提交
920 921 922
        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
        return 0;
    }
923 924 925
    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
bellard 已提交
926
        status2 = 0x04;
927 928
    if (size > fdctrl->data_len)
	size = fdctrl->data_len;
929
    if (cur_drv->bs == NULL) {
930 931 932 933 934
	if (fdctrl->data_dir == FD_DIR_WRITE)
	    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
	else
	    fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
	len = 0;
935 936
        goto transfer_error;
    }
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) {
        len = size - fdctrl->data_pos;
        if (len + rel_pos > FD_SECTOR_LEN)
            len = FD_SECTOR_LEN - rel_pos;
        FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x "
                       "(%d-0x%08x 0x%08x)\n", len, size, fdctrl->data_pos,
                       fdctrl->data_len, fdctrl->cur_drv, cur_drv->head,
                       cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
                       fd_sector(cur_drv) * 512, addr);
        if (fdctrl->data_dir != FD_DIR_WRITE ||
	    len < FD_SECTOR_LEN || rel_pos != 0) {
            /* READ & SCAN commands and realign to a sector for WRITE */
            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
			  fdctrl->fifo, 1) < 0) {
B
bellard 已提交
952 953 954
                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
                               fd_sector(cur_drv));
                /* Sure, image size is too small... */
955
                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
B
bellard 已提交
956
            }
957
        }
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
	switch (fdctrl->data_dir) {
	case FD_DIR_READ:
	    /* READ commands */
	    cpu_physical_memory_write(addr + fdctrl->data_pos,
				      fdctrl->fifo + rel_pos, len);
	    break;
	case FD_DIR_WRITE:
            /* WRITE commands */
            cpu_physical_memory_read(addr + fdctrl->data_pos,
				     fdctrl->fifo + rel_pos, len);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
			   fdctrl->fifo, 1) < 0) {
                FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
                fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
                goto transfer_error;
973
            }
974 975 976 977 978 979 980 981 982
	    break;
	default:
	    /* SCAN commands */
            {
		uint8_t tmpbuf[FD_SECTOR_LEN];
                int ret;
                cpu_physical_memory_read(addr + fdctrl->data_pos,
                                         tmpbuf, len);
                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
B
bellard 已提交
983 984 985 986
                if (ret == 0) {
                    status2 = 0x08;
                    goto end_transfer;
                }
987 988
                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
B
bellard 已提交
989 990 991 992
                    status2 = 0x00;
                    goto end_transfer;
                }
            }
993
	    break;
B
bellard 已提交
994
        }
995 996 997
	fdctrl->data_pos += len;
	rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
        if (rel_pos == 0) {
B
bellard 已提交
998
            /* Seek to next sector */
999 1000 1001 1002
	    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
			   cur_drv->head, cur_drv->track, cur_drv->sect,
			   fd_sector(cur_drv),
			   fdctrl->data_pos - size);
1003 1004 1005 1006
            /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
               error in fact */
            if (cur_drv->sect >= cur_drv->last_sect ||
                cur_drv->sect == fdctrl->eot) {
1007 1008 1009 1010
		cur_drv->sect = 1;
		if (FD_MULTI_TRACK(fdctrl->data_state)) {
		    if (cur_drv->head == 0 &&
			(cur_drv->flags & FDISK_DBL_SIDES) != 0) {	
1011 1012 1013
                        cur_drv->head = 1;
                    } else {
                        cur_drv->head = 0;
1014 1015 1016
			cur_drv->track++;
			if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
			    break;
1017 1018 1019 1020
                    }
                } else {
                    cur_drv->track++;
                    break;
B
bellard 已提交
1021
                }
1022 1023 1024
		FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
			       cur_drv->head, cur_drv->track,
			       cur_drv->sect, fd_sector(cur_drv));
1025 1026
            } else {
                cur_drv->sect++;
B
bellard 已提交
1027 1028 1029 1030
            }
        }
    }
end_transfer:
1031 1032 1033 1034 1035 1036
    len = fdctrl->data_pos - start_pos;
    FLOPPY_DPRINTF("end transfer %d %d %d\n",
		   fdctrl->data_pos, len, fdctrl->data_len);
    if (fdctrl->data_dir == FD_DIR_SCANE ||
        fdctrl->data_dir == FD_DIR_SCANL ||
        fdctrl->data_dir == FD_DIR_SCANH)
B
bellard 已提交
1037
        status2 = 0x08;
1038
    if (FD_DID_SEEK(fdctrl->data_state))
B
bellard 已提交
1039
        status0 |= 0x20;
1040 1041
    fdctrl->data_len -= len;
    //    if (fdctrl->data_len == 0)
1042
    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
B
bellard 已提交
1043 1044
transfer_error:

1045
    return len;
B
bellard 已提交
1046 1047 1048
}

/* Data register : 0x05 */
1049
static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
B
bellard 已提交
1050
{
1051
    fdrive_t *cur_drv;
B
bellard 已提交
1052 1053 1054
    uint32_t retval = 0;
    int pos, len;

1055 1056 1057
    cur_drv = get_cur_drv(fdctrl);
    fdctrl->state &= ~FD_CTRL_SLEEP;
    if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) {
B
bellard 已提交
1058 1059 1060
        FLOPPY_ERROR("can't read data in CMD state\n");
        return 0;
    }
1061 1062
    pos = fdctrl->data_pos;
    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
B
bellard 已提交
1063 1064
        pos %= FD_SECTOR_LEN;
        if (pos == 0) {
1065
            len = fdctrl->data_len - fdctrl->data_pos;
B
bellard 已提交
1066 1067 1068
            if (len > FD_SECTOR_LEN)
                len = FD_SECTOR_LEN;
            bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1069
                      fdctrl->fifo, len);
B
bellard 已提交
1070 1071
        }
    }
1072 1073 1074
    retval = fdctrl->fifo[pos];
    if (++fdctrl->data_pos == fdctrl->data_len) {
        fdctrl->data_pos = 0;
1075
        /* Switch from transfer mode to status mode
B
bellard 已提交
1076 1077
         * then from status mode to command mode
         */
1078
        if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1079
            fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1080
        } else {
1081
            fdctrl_reset_fifo(fdctrl);
1082 1083
            fdctrl_reset_irq(fdctrl);
        }
B
bellard 已提交
1084 1085 1086 1087 1088 1089
    }
    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);

    return retval;
}

1090
static void fdctrl_format_sector (fdctrl_t *fdctrl)
B
bellard 已提交
1091
{
1092 1093 1094
    fdrive_t *cur_drv;
    uint8_t kh, kt, ks;
    int did_seek;
B
bellard 已提交
1095

1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
    fdctrl->cur_drv = fdctrl->fifo[1] & 1;
    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",
                   fdctrl->cur_drv, kh, kt, ks,
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
    did_seek = 0;
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
    case 2:
        /* sect too big */
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
        fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 1:
        did_seek = 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) {
        FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv));
        fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
    } else {
	if (cur_drv->sect == cur_drv->last_sect) {
	    fdctrl->data_state &= ~FD_STATE_FORMAT;
	    /* Last sector done */
	    if (FD_DID_SEEK(fdctrl->data_state))
		fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
	    else
		fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
	} else {
	    /* More to do */
	    fdctrl->data_pos = 0;
	    fdctrl->data_len = 4;
	}
    }
}

static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
{
    fdrive_t *cur_drv;

    cur_drv = get_cur_drv(fdctrl);
B
bellard 已提交
1160
    /* Reset mode */
1161
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
1162
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
1163 1164
        return;
    }
1165 1166
    fdctrl->state &= ~FD_CTRL_SLEEP;
    if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) {
B
bellard 已提交
1167 1168 1169 1170
        FLOPPY_ERROR("can't write data in status mode\n");
        return;
    }
    /* Is it write command time ? */
1171
    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
B
bellard 已提交
1172
        /* FIFO data write */
1173 1174 1175
        fdctrl->fifo[fdctrl->data_pos++] = value;
        if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
            fdctrl->data_pos == fdctrl->data_len) {
B
bellard 已提交
1176
            bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1177
                       fdctrl->fifo, FD_SECTOR_LEN);
B
bellard 已提交
1178
        }
1179
        /* Switch from transfer mode to status mode
B
bellard 已提交
1180 1181
         * then from status mode to command mode
         */
1182 1183
        if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA)
            fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
B
bellard 已提交
1184 1185
        return;
    }
1186
    if (fdctrl->data_pos == 0) {
B
bellard 已提交
1187 1188 1189 1190 1191 1192
        /* Command */
        switch (value & 0x5F) {
        case 0x46:
            /* READ variants */
            FLOPPY_DPRINTF("READ command\n");
            /* 8 parameters cmd */
1193
            fdctrl->data_len = 9;
B
bellard 已提交
1194 1195 1196 1197 1198
            goto enqueue;
        case 0x4C:
            /* READ_DELETED variants */
            FLOPPY_DPRINTF("READ_DELETED command\n");
            /* 8 parameters cmd */
1199
            fdctrl->data_len = 9;
B
bellard 已提交
1200 1201 1202 1203 1204
            goto enqueue;
        case 0x50:
            /* SCAN_EQUAL variants */
            FLOPPY_DPRINTF("SCAN_EQUAL command\n");
            /* 8 parameters cmd */
1205
            fdctrl->data_len = 9;
B
bellard 已提交
1206 1207 1208 1209 1210
            goto enqueue;
        case 0x56:
            /* VERIFY variants */
            FLOPPY_DPRINTF("VERIFY command\n");
            /* 8 parameters cmd */
1211
            fdctrl->data_len = 9;
B
bellard 已提交
1212 1213 1214 1215 1216
            goto enqueue;
        case 0x59:
            /* SCAN_LOW_OR_EQUAL variants */
            FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n");
            /* 8 parameters cmd */
1217
            fdctrl->data_len = 9;
B
bellard 已提交
1218 1219 1220 1221 1222
            goto enqueue;
        case 0x5D:
            /* SCAN_HIGH_OR_EQUAL variants */
            FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n");
            /* 8 parameters cmd */
1223
            fdctrl->data_len = 9;
B
bellard 已提交
1224 1225 1226 1227 1228 1229 1230 1231 1232
            goto enqueue;
        default:
            break;
        }
        switch (value & 0x7F) {
        case 0x45:
            /* WRITE variants */
            FLOPPY_DPRINTF("WRITE command\n");
            /* 8 parameters cmd */
1233
            fdctrl->data_len = 9;
B
bellard 已提交
1234 1235 1236 1237 1238
            goto enqueue;
        case 0x49:
            /* WRITE_DELETED variants */
            FLOPPY_DPRINTF("WRITE_DELETED command\n");
            /* 8 parameters cmd */
1239
            fdctrl->data_len = 9;
B
bellard 已提交
1240 1241 1242 1243 1244 1245 1246 1247 1248
            goto enqueue;
        default:
            break;
        }
        switch (value) {
        case 0x03:
            /* SPECIFY */
            FLOPPY_DPRINTF("SPECIFY command\n");
            /* 1 parameter cmd */
1249
            fdctrl->data_len = 3;
B
bellard 已提交
1250 1251 1252 1253 1254
            goto enqueue;
        case 0x04:
            /* SENSE_DRIVE_STATUS */
            FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n");
            /* 1 parameter cmd */
1255
            fdctrl->data_len = 2;
B
bellard 已提交
1256 1257 1258 1259 1260
            goto enqueue;
        case 0x07:
            /* RECALIBRATE */
            FLOPPY_DPRINTF("RECALIBRATE command\n");
            /* 1 parameter cmd */
1261
            fdctrl->data_len = 2;
B
bellard 已提交
1262 1263 1264 1265
            goto enqueue;
        case 0x08:
            /* SENSE_INTERRUPT_STATUS */
            FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n",
1266
                           fdctrl->int_status);
B
bellard 已提交
1267
            /* No parameters cmd: returns status if no interrupt */
B
bellard 已提交
1268
#if 0
1269 1270
            fdctrl->fifo[0] =
                fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv;
B
bellard 已提交
1271 1272 1273 1274 1275 1276 1277
#else
            /* XXX: int_status handling is broken for read/write
               commands, so we do this hack. It should be suppressed
               ASAP */
            fdctrl->fifo[0] =
                0x20 | (cur_drv->head << 2) | fdctrl->cur_drv;
#endif
1278 1279 1280 1281
            fdctrl->fifo[1] = cur_drv->track;
            fdctrl_set_fifo(fdctrl, 2, 0);
	    fdctrl_reset_irq(fdctrl);
	    fdctrl->int_status = 0xC0;
B
bellard 已提交
1282 1283 1284 1285 1286
            return;
        case 0x0E:
            /* DUMPREG */
            FLOPPY_DPRINTF("DUMPREG command\n");
            /* Drives position */
1287 1288 1289 1290
            fdctrl->fifo[0] = drv0(fdctrl)->track;
            fdctrl->fifo[1] = drv1(fdctrl)->track;
            fdctrl->fifo[2] = 0;
            fdctrl->fifo[3] = 0;
B
bellard 已提交
1291
            /* timers */
1292 1293 1294 1295
            fdctrl->fifo[4] = fdctrl->timer0;
            fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en;
            fdctrl->fifo[6] = cur_drv->last_sect;
            fdctrl->fifo[7] = (fdctrl->lock << 7) |
B
bellard 已提交
1296
                    (cur_drv->perpendicular << 2);
1297 1298 1299
            fdctrl->fifo[8] = fdctrl->config;
            fdctrl->fifo[9] = fdctrl->precomp_trk;
            fdctrl_set_fifo(fdctrl, 10, 0);
B
bellard 已提交
1300 1301 1302 1303 1304
            return;
        case 0x0F:
            /* SEEK */
            FLOPPY_DPRINTF("SEEK command\n");
            /* 2 parameters cmd */
1305
            fdctrl->data_len = 3;
B
bellard 已提交
1306 1307 1308 1309 1310
            goto enqueue;
        case 0x10:
            /* VERSION */
            FLOPPY_DPRINTF("VERSION command\n");
            /* No parameters cmd */
B
bellard 已提交
1311
            /* Controller's version */
1312 1313
            fdctrl->fifo[0] = fdctrl->version;
            fdctrl_set_fifo(fdctrl, 1, 1);
B
bellard 已提交
1314 1315 1316 1317 1318
            return;
        case 0x12:
            /* PERPENDICULAR_MODE */
            FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n");
            /* 1 parameter cmd */
1319
            fdctrl->data_len = 2;
B
bellard 已提交
1320 1321 1322 1323 1324
            goto enqueue;
        case 0x13:
            /* CONFIGURE */
            FLOPPY_DPRINTF("CONFIGURE command\n");
            /* 3 parameters cmd */
1325
            fdctrl->data_len = 4;
B
bellard 已提交
1326 1327 1328 1329 1330
            goto enqueue;
        case 0x14:
            /* UNLOCK */
            FLOPPY_DPRINTF("UNLOCK command\n");
            /* No parameters cmd */
1331 1332 1333
            fdctrl->lock = 0;
            fdctrl->fifo[0] = 0;
            fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
1334 1335 1336 1337 1338
            return;
        case 0x17:
            /* POWERDOWN_MODE */
            FLOPPY_DPRINTF("POWERDOWN_MODE command\n");
            /* 2 parameters cmd */
1339
            fdctrl->data_len = 3;
B
bellard 已提交
1340 1341 1342 1343 1344
            goto enqueue;
        case 0x18:
            /* PART_ID */
            FLOPPY_DPRINTF("PART_ID command\n");
            /* No parameters cmd */
1345 1346
            fdctrl->fifo[0] = 0x41; /* Stepping 1 */
            fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
1347 1348 1349 1350 1351
            return;
        case 0x2C:
            /* SAVE */
            FLOPPY_DPRINTF("SAVE command\n");
            /* No parameters cmd */
1352 1353
            fdctrl->fifo[0] = 0;
            fdctrl->fifo[1] = 0;
B
bellard 已提交
1354
            /* Drives position */
1355 1356 1357 1358
            fdctrl->fifo[2] = drv0(fdctrl)->track;
            fdctrl->fifo[3] = drv1(fdctrl)->track;
            fdctrl->fifo[4] = 0;
            fdctrl->fifo[5] = 0;
B
bellard 已提交
1359
            /* timers */
1360 1361 1362 1363
            fdctrl->fifo[6] = fdctrl->timer0;
            fdctrl->fifo[7] = fdctrl->timer1;
            fdctrl->fifo[8] = cur_drv->last_sect;
            fdctrl->fifo[9] = (fdctrl->lock << 7) |
B
bellard 已提交
1364
                    (cur_drv->perpendicular << 2);
1365 1366 1367 1368 1369 1370
            fdctrl->fifo[10] = fdctrl->config;
            fdctrl->fifo[11] = fdctrl->precomp_trk;
            fdctrl->fifo[12] = fdctrl->pwrd;
            fdctrl->fifo[13] = 0;
            fdctrl->fifo[14] = 0;
            fdctrl_set_fifo(fdctrl, 15, 1);
B
bellard 已提交
1371 1372 1373 1374 1375
            return;
        case 0x33:
            /* OPTION */
            FLOPPY_DPRINTF("OPTION command\n");
            /* 1 parameter cmd */
1376
            fdctrl->data_len = 2;
B
bellard 已提交
1377 1378 1379 1380 1381
            goto enqueue;
        case 0x42:
            /* READ_TRACK */
            FLOPPY_DPRINTF("READ_TRACK command\n");
            /* 8 parameters cmd */
1382
            fdctrl->data_len = 9;
B
bellard 已提交
1383 1384 1385 1386 1387
            goto enqueue;
        case 0x4A:
            /* READ_ID */
            FLOPPY_DPRINTF("READ_ID command\n");
            /* 1 parameter cmd */
1388
            fdctrl->data_len = 2;
B
bellard 已提交
1389 1390 1391 1392 1393
            goto enqueue;
        case 0x4C:
            /* RESTORE */
            FLOPPY_DPRINTF("RESTORE command\n");
            /* 17 parameters cmd */
1394
            fdctrl->data_len = 18;
B
bellard 已提交
1395 1396 1397 1398 1399
            goto enqueue;
        case 0x4D:
            /* FORMAT_TRACK */
            FLOPPY_DPRINTF("FORMAT_TRACK command\n");
            /* 5 parameters cmd */
1400
            fdctrl->data_len = 6;
B
bellard 已提交
1401 1402 1403 1404 1405
            goto enqueue;
        case 0x8E:
            /* DRIVE_SPECIFICATION_COMMAND */
            FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n");
            /* 5 parameters cmd */
1406
            fdctrl->data_len = 6;
B
bellard 已提交
1407 1408 1409 1410 1411
            goto enqueue;
        case 0x8F:
            /* RELATIVE_SEEK_OUT */
            FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n");
            /* 2 parameters cmd */
1412
            fdctrl->data_len = 3;
B
bellard 已提交
1413 1414 1415 1416 1417
            goto enqueue;
        case 0x94:
            /* LOCK */
            FLOPPY_DPRINTF("LOCK command\n");
            /* No parameters cmd */
1418 1419 1420
            fdctrl->lock = 1;
            fdctrl->fifo[0] = 0x10;
            fdctrl_set_fifo(fdctrl, 1, 1);
B
bellard 已提交
1421 1422 1423 1424 1425
            return;
        case 0xCD:
            /* FORMAT_AND_WRITE */
            FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n");
            /* 10 parameters cmd */
1426
            fdctrl->data_len = 11;
B
bellard 已提交
1427 1428 1429 1430 1431
            goto enqueue;
        case 0xCF:
            /* RELATIVE_SEEK_IN */
            FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n");
            /* 2 parameters cmd */
1432
            fdctrl->data_len = 3;
B
bellard 已提交
1433 1434 1435 1436
            goto enqueue;
        default:
            /* Unknown command */
            FLOPPY_ERROR("unknown command: 0x%02x\n", value);
1437
            fdctrl_unimplemented(fdctrl);
B
bellard 已提交
1438 1439 1440 1441
            return;
        }
    }
enqueue:
1442 1443 1444
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
    fdctrl->fifo[fdctrl->data_pos] = value;
    if (++fdctrl->data_pos == fdctrl->data_len) {
B
bellard 已提交
1445 1446 1447
        /* We now have all parameters
         * and will be able to treat the command
         */
1448 1449 1450 1451 1452
	if (fdctrl->data_state & FD_STATE_FORMAT) {
	    fdctrl_format_sector(fdctrl);
	    return;
	}
        switch (fdctrl->fifo[0] & 0x1F) {
B
bellard 已提交
1453 1454 1455 1456
        case 0x06:
        {
            /* READ variants */
            FLOPPY_DPRINTF("treat READ command\n");
1457
            fdctrl_start_transfer(fdctrl, FD_DIR_READ);
B
bellard 已提交
1458 1459 1460 1461 1462 1463
            return;
        }
        case 0x0C:
            /* READ_DELETED variants */
//            FLOPPY_DPRINTF("treat READ_DELETED command\n");
            FLOPPY_ERROR("treat READ_DELETED command\n");
1464
            fdctrl_start_transfer_del(fdctrl, FD_DIR_READ);
B
bellard 已提交
1465 1466 1467 1468 1469
            return;
        case 0x16:
            /* VERIFY variants */
//            FLOPPY_DPRINTF("treat VERIFY command\n");
            FLOPPY_ERROR("treat VERIFY command\n");
1470
            fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
B
bellard 已提交
1471 1472 1473 1474 1475
            return;
        case 0x10:
            /* SCAN_EQUAL variants */
//            FLOPPY_DPRINTF("treat SCAN_EQUAL command\n");
            FLOPPY_ERROR("treat SCAN_EQUAL command\n");
1476
            fdctrl_start_transfer(fdctrl, FD_DIR_SCANE);
B
bellard 已提交
1477 1478 1479 1480 1481
            return;
        case 0x19:
            /* SCAN_LOW_OR_EQUAL variants */
//            FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n");
            FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n");
1482
            fdctrl_start_transfer(fdctrl, FD_DIR_SCANL);
B
bellard 已提交
1483 1484 1485 1486 1487
            return;
        case 0x1D:
            /* SCAN_HIGH_OR_EQUAL variants */
//            FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n");
            FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n");
1488
            fdctrl_start_transfer(fdctrl, FD_DIR_SCANH);
B
bellard 已提交
1489 1490 1491 1492
            return;
        default:
            break;
        }
1493
        switch (fdctrl->fifo[0] & 0x3F) {
B
bellard 已提交
1494 1495
        case 0x05:
            /* WRITE variants */
1496 1497
            FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]);
            fdctrl_start_transfer(fdctrl, FD_DIR_WRITE);
B
bellard 已提交
1498 1499 1500 1501 1502
            return;
        case 0x09:
            /* WRITE_DELETED variants */
//            FLOPPY_DPRINTF("treat WRITE_DELETED command\n");
            FLOPPY_ERROR("treat WRITE_DELETED command\n");
1503
            fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE);
B
bellard 已提交
1504 1505 1506 1507
            return;
        default:
            break;
        }
1508
        switch (fdctrl->fifo[0]) {
B
bellard 已提交
1509 1510 1511
        case 0x03:
            /* SPECIFY */
            FLOPPY_DPRINTF("treat SPECIFY command\n");
1512
            fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
B
bellard 已提交
1513
            fdctrl->timer1 = fdctrl->fifo[2] >> 1;
1514
	    fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ;
B
bellard 已提交
1515
            /* No result back */
1516
            fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1517 1518 1519 1520
            break;
        case 0x04:
            /* SENSE_DRIVE_STATUS */
            FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n");
1521 1522 1523
            fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    cur_drv = get_cur_drv(fdctrl);
            cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
B
bellard 已提交
1524
            /* 1 Byte status back */
1525
            fdctrl->fifo[0] = (cur_drv->ro << 6) |
B
bellard 已提交
1526
                (cur_drv->track == 0 ? 0x10 : 0x00) |
1527 1528 1529
                (cur_drv->head << 2) |
                fdctrl->cur_drv |
                0x28;
1530
            fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
1531 1532 1533 1534
            break;
        case 0x07:
            /* RECALIBRATE */
            FLOPPY_DPRINTF("treat RECALIBRATE command\n");
1535 1536
            fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    cur_drv = get_cur_drv(fdctrl);
B
bellard 已提交
1537
            fd_recalibrate(cur_drv);
1538
	    fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1539
            /* Raise Interrupt */
1540
	    fdctrl_raise_irq(fdctrl, 0x20);
B
bellard 已提交
1541 1542 1543 1544
            break;
        case 0x0F:
            /* SEEK */
            FLOPPY_DPRINTF("treat SEEK command\n");
1545 1546 1547 1548
            fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    cur_drv = get_cur_drv(fdctrl);
	    fd_start(cur_drv);
            if (fdctrl->fifo[2] <= cur_drv->track)
B
bellard 已提交
1549 1550 1551
                cur_drv->dir = 1;
            else
                cur_drv->dir = 0;
1552 1553 1554
	    fdctrl_reset_fifo(fdctrl);
            if (fdctrl->fifo[2] > cur_drv->max_track) {
                fdctrl_raise_irq(fdctrl, 0x60);
B
bellard 已提交
1555
            } else {
1556
                cur_drv->track = fdctrl->fifo[2];
B
bellard 已提交
1557
                /* Raise Interrupt */
1558
                fdctrl_raise_irq(fdctrl, 0x20);
B
bellard 已提交
1559 1560 1561 1562 1563
            }
            break;
        case 0x12:
            /* PERPENDICULAR_MODE */
            FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n");
1564 1565
            if (fdctrl->fifo[1] & 0x80)
                cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
B
bellard 已提交
1566
            /* No result back */
1567
            fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1568 1569 1570 1571
            break;
        case 0x13:
            /* CONFIGURE */
            FLOPPY_DPRINTF("treat CONFIGURE command\n");
1572 1573
            fdctrl->config = fdctrl->fifo[2];
            fdctrl->precomp_trk =  fdctrl->fifo[3];
B
bellard 已提交
1574
            /* No result back */
1575
            fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1576 1577 1578 1579
            break;
        case 0x17:
            /* POWERDOWN_MODE */
            FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n");
1580 1581 1582
            fdctrl->pwrd = fdctrl->fifo[1];
            fdctrl->fifo[0] = fdctrl->fifo[1];
            fdctrl_set_fifo(fdctrl, 1, 1);
B
bellard 已提交
1583 1584 1585 1586 1587
            break;
        case 0x33:
            /* OPTION */
            FLOPPY_DPRINTF("treat OPTION command\n");
            /* No result back */
1588
            fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1589 1590 1591 1592 1593
            break;
        case 0x42:
            /* READ_TRACK */
//            FLOPPY_DPRINTF("treat READ_TRACK command\n");
            FLOPPY_ERROR("treat READ_TRACK command\n");
1594
            fdctrl_start_transfer(fdctrl, FD_DIR_READ);
B
bellard 已提交
1595 1596 1597
            break;
        case 0x4A:
                /* READ_ID */
1598
            FLOPPY_DPRINTF("treat READ_ID command\n");
1599
            /* XXX: should set main status register to busy */
1600
            cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
1601 1602
            qemu_mod_timer(fdctrl->result_timer, 
                           qemu_get_clock(vm_clock) + (ticks_per_sec / 50));
B
bellard 已提交
1603 1604 1605 1606 1607
            break;
        case 0x4C:
            /* RESTORE */
            FLOPPY_DPRINTF("treat RESTORE command\n");
            /* Drives position */
1608 1609
            drv0(fdctrl)->track = fdctrl->fifo[3];
            drv1(fdctrl)->track = fdctrl->fifo[4];
B
bellard 已提交
1610
            /* timers */
1611 1612 1613 1614 1615 1616 1617 1618 1619
            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
bellard 已提交
1620 1621 1622
            break;
        case 0x4D:
            /* FORMAT_TRACK */
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
	    FLOPPY_DPRINTF("treat FORMAT_TRACK command\n");
	    fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    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
	    /* Bochs BIOS is buggy and don't send format informations
	     * for each sector. So, pretend all's done right now...
	     */
	    fdctrl->data_state &= ~FD_STATE_FORMAT;
	    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
B
bellard 已提交
1646 1647 1648 1649
            break;
        case 0x8E:
            /* DRIVE_SPECIFICATION_COMMAND */
            FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n");
1650
            if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
B
bellard 已提交
1651
                /* Command parameters done */
1652 1653 1654 1655 1656
                if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
                    fdctrl->fifo[0] = fdctrl->fifo[1];
                    fdctrl->fifo[2] = 0;
                    fdctrl->fifo[3] = 0;
                    fdctrl_set_fifo(fdctrl, 4, 1);
B
bellard 已提交
1657
                } else {
1658
                    fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
1659
                }
1660
            } else if (fdctrl->data_len > 7) {
B
bellard 已提交
1661
                /* ERROR */
1662 1663 1664
                fdctrl->fifo[0] = 0x80 |
                    (cur_drv->head << 2) | fdctrl->cur_drv;
                fdctrl_set_fifo(fdctrl, 1, 1);
B
bellard 已提交
1665 1666 1667 1668 1669
            }
            break;
        case 0x8F:
            /* RELATIVE_SEEK_OUT */
            FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n");
1670 1671 1672
            fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    cur_drv = get_cur_drv(fdctrl);
	    fd_start(cur_drv);
B
bellard 已提交
1673
                cur_drv->dir = 0;
1674 1675 1676 1677
            if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
		cur_drv->track = cur_drv->max_track - 1;
            } else {
                cur_drv->track += fdctrl->fifo[2];
B
bellard 已提交
1678
            }
1679 1680
	    fdctrl_reset_fifo(fdctrl);
	    fdctrl_raise_irq(fdctrl, 0x20);
B
bellard 已提交
1681 1682 1683 1684 1685
            break;
        case 0xCD:
            /* FORMAT_AND_WRITE */
//                FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n");
            FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n");
1686
            fdctrl_unimplemented(fdctrl);
B
bellard 已提交
1687 1688 1689 1690
            break;
        case 0xCF:
                /* RELATIVE_SEEK_IN */
            FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n");
1691 1692 1693
            fdctrl->cur_drv = fdctrl->fifo[1] & 1;
	    cur_drv = get_cur_drv(fdctrl);
	    fd_start(cur_drv);
B
bellard 已提交
1694
                cur_drv->dir = 1;
1695 1696 1697 1698
            if (fdctrl->fifo[2] > cur_drv->track) {
		cur_drv->track = 0;
            } else {
                cur_drv->track -= fdctrl->fifo[2];
B
bellard 已提交
1699
            }
1700 1701 1702
	    fdctrl_reset_fifo(fdctrl);
	    /* Raise Interrupt */
	    fdctrl_raise_irq(fdctrl, 0x20);
B
bellard 已提交
1703 1704 1705 1706
            break;
        }
    }
}
1707 1708 1709 1710 1711 1712

static void fdctrl_result_timer(void *opaque)
{
    fdctrl_t *fdctrl = opaque;
    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}