fdc.c 56.4 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
 * 
 * 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.
 */
B
bellard 已提交
24 25 26 27
/*
 * The controller is used in Sun4m systems in a slightly different
 * way. There are changes in DOR register and DMA is not available.
 */
B
bellard 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#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            */
56 57
    FDRIVE_DISK_USER  = 0x04, /* User defined geometry  */
    FDRIVE_DISK_NONE  = 0x05, /* No disk                */
B
bellard 已提交
58 59 60 61 62 63 64 65 66
} 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;

67 68 69 70 71 72 73 74 75
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 已提交
76 77 78 79
typedef struct fdrive_t {
    BlockDriverState *bs;
    /* Drive status */
    fdrive_type_t drive;
80
    fdrive_flags_t drflags;
B
bellard 已提交
81 82 83 84 85 86 87 88 89
    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 */
90
    fdisk_flags_t flags;
B
bellard 已提交
91 92
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
93
    uint16_t bps;             /* Bytes per sector       */
B
bellard 已提交
94 95 96
    uint8_t ro;               /* Is read-only           */
} fdrive_t;

B
bellard 已提交
97
#ifdef TARGET_SPARC
B
bellard 已提交
98
/* XXX: suppress those hacks */
B
bellard 已提交
99 100
#define DMA_read_memory(a,b,c,d)
#define DMA_write_memory(a,b,c,d)
B
bellard 已提交
101 102 103 104 105
void DMA_register_channel (int nchan,
                           DMA_transfer_handler transfer_handler,
                           void *opaque)
{
}
B
bellard 已提交
106 107 108 109 110 111
#define DMA_hold_DREQ(a)
#define DMA_release_DREQ(a)
#define DMA_get_channel_mode(a) (0)
#define DMA_schedule(a)
#endif

112
static void fd_init (fdrive_t *drv, BlockDriverState *bs)
B
bellard 已提交
113 114
{
    /* Drive */
115
    drv->bs = bs;
B
bellard 已提交
116
    drv->drive = FDRIVE_DRV_NONE;
117
    drv->drflags = 0;
B
bellard 已提交
118 119
    drv->perpendicular = 0;
    /* Disk */
120
    drv->last_sect = 0;
B
bellard 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    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;
140 141 142 143
    int ret;

    if (track > drv->max_track ||
	(head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
144 145 146 147
        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 已提交
148 149 150
        return 2;
    }
    if (sect > drv->last_sect) {
151 152 153 154
        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 已提交
155 156 157
        return 3;
    }
    sector = _fd_sector(head, track, sect, drv->last_sect);
158
    ret = 0;
B
bellard 已提交
159 160 161 162 163 164 165 166 167
    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;
168 169
	if (drv->track != track)
	    ret = 1;
B
bellard 已提交
170 171 172 173
        drv->track = track;
        drv->sect = sect;
    }

174
    return ret;
B
bellard 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187
}

/* 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;
}

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 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
/* 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 已提交
245
/* Revalidate a disk drive after a disk change */
246
static void fd_revalidate (fdrive_t *drv)
B
bellard 已提交
247
{
248 249 250
    fd_format_t *parse;
    int64_t nb_sectors, size;
    int i, first_match, match;
251
    int nb_heads, max_track, last_sect, ro;
B
bellard 已提交
252 253

    FLOPPY_DPRINTF("revalidate\n");
254
    drv->drflags &= ~FDRIVE_REVALIDATE;
255
    if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
256
	ro = bdrv_is_read_only(drv->bs);
257
	bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
258
	if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
259 260
	    FLOPPY_DPRINTF("User defined disk (%d %d %d)",
                           nb_heads - 1, max_track, last_sect);
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	} 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;
292 293
	    FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
                           nb_heads, max_track, last_sect, ro ? "ro" : "rw");
294
	}
295 296 297 298 299 300 301 302
	    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 已提交
303
    } else {
304
	FLOPPY_DPRINTF("No disk in drive\n");
305 306 307
        drv->last_sect = 0;
	drv->max_track = 0;
	drv->flags &= ~FDISK_DBL_SIDES;
B
bellard 已提交
308
    }
309
    drv->drflags |= FDRIVE_REVALIDATE;
310 311
}

B
bellard 已提交
312 313 314
/* Motor control */
static void fd_start (fdrive_t *drv)
{
315
    drv->drflags |= FDRIVE_MOTOR_ON;
B
bellard 已提交
316 317 318 319
}

static void fd_stop (fdrive_t *drv)
{
320
    drv->drflags &= ~FDRIVE_MOTOR_ON;
B
bellard 已提交
321 322 323 324 325 326 327 328 329 330
}

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

/********************************************************/
B
bellard 已提交
331
/* Intel 82078 floppy disk controller emulation          */
B
bellard 已提交
332

333 334
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
B
bellard 已提交
335 336
static int fdctrl_transfer_handler (void *opaque, int nchan,
                                    int dma_pos, int dma_len);
337
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
338
static void fdctrl_result_timer(void *opaque);
339 340 341 342 343 344 345 346 347 348 349

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 已提交
350 351

enum {
352
    FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */
B
bellard 已提交
353
    FD_CTRL_RESET  = 0x02,
354 355
    FD_CTRL_SLEEP  = 0x04, /* XXX: suppress that */
    FD_CTRL_BUSY   = 0x08, /* dma transfer in progress */
B
bellard 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    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,
374
    FD_STATE_FORMAT = 0x40,
B
bellard 已提交
375 376 377
};

#define FD_STATE(state) ((state) & FD_STATE_STATE)
378 379
#define FD_SET_STATE(state, new_state) \
do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)
B
bellard 已提交
380 381
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
382
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
B
bellard 已提交
383

384 385
struct fdctrl_t {
    fdctrl_t *fdctrl;
B
bellard 已提交
386
    /* Controller's identification */
B
bellard 已提交
387 388 389 390
    uint8_t version;
    /* HW */
    int irq_lvl;
    int dma_chann;
391
    uint32_t io_base;
B
bellard 已提交
392
    /* Controller state */
393
    QEMUTimer *result_timer;
B
bellard 已提交
394 395 396 397 398 399 400 401 402 403 404
    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;
405
    uint8_t eot; /* last wanted sector */
B
bellard 已提交
406 407 408 409 410 411 412 413 414 415 416 417
    /* 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];
418 419 420 421 422 423 424
};

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

425 426
    switch (reg & 0x07) {
    case 0x01:
427
	retval = fdctrl_read_statusB(fdctrl);
428 429
	break;
    case 0x02:
430
	retval = fdctrl_read_dor(fdctrl);
431 432
	break;
    case 0x03:
433
        retval = fdctrl_read_tape(fdctrl);
434 435
	break;
    case 0x04:
436
        retval = fdctrl_read_main_status(fdctrl);
437 438
	break;
    case 0x05:
439
        retval = fdctrl_read_data(fdctrl);
440 441
	break;
    case 0x07:
442
        retval = fdctrl_read_dir(fdctrl);
443 444
	break;
    default:
445
	retval = (uint32_t)(-1);
446 447
	break;
    }
448
    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
449 450 451 452 453 454 455 456

    return retval;
}

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

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

459 460
    switch (reg & 0x07) {
    case 0x02:
461
	fdctrl_write_dor(fdctrl, value);
462 463
	break;
    case 0x03:
464
        fdctrl_write_tape(fdctrl, value);
465 466
	break;
    case 0x04:
467
        fdctrl_write_rate(fdctrl, value);
468 469
	break;
    case 0x05:
470
        fdctrl_write_data(fdctrl, value);
471 472 473 474
	break;
    default:
	break;
    }
475 476
}

B
bellard 已提交
477 478 479 480 481 482 483 484 485 486 487
static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
{
    return fdctrl_read(opaque, reg);
}

static void fdctrl_write_mem (void *opaque, 
                              target_phys_addr_t reg, uint32_t value)
{
    fdctrl_write(opaque, reg, value);
}

B
bellard 已提交
488
static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
B
bellard 已提交
489 490 491
    fdctrl_read_mem,
    fdctrl_read_mem,
    fdctrl_read_mem,
B
bellard 已提交
492 493 494
};

static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
B
bellard 已提交
495 496 497
    fdctrl_write_mem,
    fdctrl_write_mem,
    fdctrl_write_mem,
B
bellard 已提交
498 499
};

500 501 502
static void fd_change_cb (void *opaque)
{
    fdrive_t *drv = opaque;
B
bellard 已提交
503

504 505 506 507 508 509 510 511
    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 已提交
512

513 514 515
fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped, 
                       uint32_t io_base,
                       BlockDriverState **fds)
B
bellard 已提交
516
{
517
    fdctrl_t *fdctrl;
B
bellard 已提交
518
    int io_mem;
B
bellard 已提交
519 520
    int i;

B
bellard 已提交
521
    FLOPPY_DPRINTF("init controller\n");
522 523 524
    fdctrl = qemu_mallocz(sizeof(fdctrl_t));
    if (!fdctrl)
        return NULL;
525 526 527
    fdctrl->result_timer = qemu_new_timer(vm_clock, 
                                          fdctrl_result_timer, fdctrl);

B
bellard 已提交
528
    fdctrl->version = 0x90; /* Intel 82078 controller */
529 530 531
    fdctrl->irq_lvl = irq_lvl;
    fdctrl->dma_chann = dma_chann;
    fdctrl->io_base = io_base;
532
    fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
533 534 535
    if (fdctrl->dma_chann != -1) {
        fdctrl->dma_en = 1;
        DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
B
bellard 已提交
536
    } else {
537
        fdctrl->dma_en = 0;
B
bellard 已提交
538
    }
539 540 541 542 543 544
    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]);
        }
545
    }
546 547
    fdctrl_reset(fdctrl, 0);
    fdctrl->state = FD_CTRL_ACTIVE;
B
bellard 已提交
548
    if (mem_mapped) {
B
bellard 已提交
549 550
        io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write, fdctrl);
        cpu_register_physical_memory(io_base, 0x08, io_mem);
B
bellard 已提交
551
    } else {
552 553 554 555
        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 已提交
556
    }
557
    for (i = 0; i < 2; i++) {
558
        fd_revalidate(&fdctrl->drives[i]);
B
bellard 已提交
559
    }
560

561
    return fdctrl;
562
}
B
bellard 已提交
563

564 565
/* XXX: may change if moved to bdrv */
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
566
{
567
    return fdctrl->drives[drive_num].drive;
B
bellard 已提交
568 569 570
}

/* Change IRQ state */
571
static void fdctrl_reset_irq (fdctrl_t *fdctrl)
B
bellard 已提交
572
{
573 574 575
    FLOPPY_DPRINTF("Reset interrupt\n");
    pic_set_irq(fdctrl->irq_lvl, 0);
    fdctrl->state &= ~FD_CTRL_INTR;
B
bellard 已提交
576 577
}

578
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status)
B
bellard 已提交
579
{
580 581 582
    if (~(fdctrl->state & FD_CTRL_INTR)) {
        pic_set_irq(fdctrl->irq_lvl, 1);
        fdctrl->state |= FD_CTRL_INTR;
B
bellard 已提交
583 584
    }
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
585
    fdctrl->int_status = status;
B
bellard 已提交
586 587
}

B
bellard 已提交
588
/* Reset controller */
589
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
B
bellard 已提交
590 591 592
{
    int i;

B
bellard 已提交
593
    FLOPPY_DPRINTF("reset controller\n");
594
    fdctrl_reset_irq(fdctrl);
B
bellard 已提交
595
    /* Initialise controller */
596
    fdctrl->cur_drv = 0;
B
bellard 已提交
597
    /* FIFO state */
598 599 600 601
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
    fdctrl->data_state = FD_STATE_CMD;
    fdctrl->data_dir = FD_DIR_WRITE;
B
bellard 已提交
602
    for (i = 0; i < MAX_FD; i++)
603 604
        fd_reset(&fdctrl->drives[i]);
    fdctrl_reset_fifo(fdctrl);
B
bellard 已提交
605
    if (do_irq)
606
        fdctrl_raise_irq(fdctrl, 0xc0);
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
}

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 已提交
622 623 624
}

/* Status B register : 0x01 (read-only) */
625
static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
B
bellard 已提交
626 627 628 629 630 631
{
    FLOPPY_DPRINTF("status register: 0x00\n");
    return 0;
}

/* Digital output register : 0x02 */
632
static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
B
bellard 已提交
633 634 635 636
{
    uint32_t retval = 0;

    /* Drive motors state indicators */
637 638 639 640
    if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
	retval |= 1 << 5;
    if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
	retval |= 1 << 4;
B
bellard 已提交
641
    /* DMA enable */
642
    retval |= fdctrl->dma_en << 3;
B
bellard 已提交
643
    /* Reset indicator */
644
    retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
B
bellard 已提交
645
    /* Selected drive */
646
    retval |= fdctrl->cur_drv;
B
bellard 已提交
647 648 649 650 651
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}

652
static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
653 654
{
    /* Reset mode */
655
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
656
        if (!(value & 0x04)) {
B
bellard 已提交
657
            FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
658 659 660 661 662 663
            return;
        }
    }
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
    /* Drive motors state indicators */
    if (value & 0x20)
664
        fd_start(drv1(fdctrl));
B
bellard 已提交
665
    else
666
        fd_stop(drv1(fdctrl));
B
bellard 已提交
667
    if (value & 0x10)
668
        fd_start(drv0(fdctrl));
B
bellard 已提交
669
    else
670
        fd_stop(drv0(fdctrl));
B
bellard 已提交
671 672
    /* DMA enable */
#if 0
673 674
    if (fdctrl->dma_chann != -1)
        fdctrl->dma_en = 1 - ((value >> 3) & 1);
B
bellard 已提交
675 676 677
#endif
    /* Reset */
    if (!(value & 0x04)) {
678
        if (!(fdctrl->state & FD_CTRL_RESET)) {
B
bellard 已提交
679
            FLOPPY_DPRINTF("controller enter RESET state\n");
680
            fdctrl->state |= FD_CTRL_RESET;
B
bellard 已提交
681 682
        }
    } else {
683
        if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
684
            FLOPPY_DPRINTF("controller out of RESET state\n");
685
            fdctrl_reset(fdctrl, 1);
686
            fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
B
bellard 已提交
687 688 689
        }
    }
    /* Selected drive */
690
    fdctrl->cur_drv = value & 1;
B
bellard 已提交
691 692 693
}

/* Tape drive register : 0x03 */
694
static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
B
bellard 已提交
695 696 697 698
{
    uint32_t retval = 0;

    /* Disk boot selection indicator */
699
    retval |= fdctrl->bootsel << 2;
B
bellard 已提交
700 701 702 703 704 705
    /* Tape indicators: never allowed */
    FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);

    return retval;
}

706
static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
707 708
{
    /* Reset mode */
709
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
710
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
711 712 713 714
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
715
    fdctrl->bootsel = (value >> 2) & 1;
B
bellard 已提交
716 717 718 719
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
720
static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
B
bellard 已提交
721 722 723
{
    uint32_t retval = 0;

724 725
    fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
    if (!(fdctrl->state & FD_CTRL_BUSY)) {
B
bellard 已提交
726 727 728
        /* Data transfer allowed */
        retval |= 0x80;
        /* Data transfer direction indicator */
729
        if (fdctrl->data_dir == FD_DIR_READ)
B
bellard 已提交
730 731 732 733
            retval |= 0x40;
    }
    /* Should handle 0x20 for SPECIFY command */
    /* Command busy indicator */
734 735
    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
        FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
B
bellard 已提交
736 737 738 739 740 741 742
        retval |= 0x10;
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
743
static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
B
bellard 已提交
744 745
{
    /* Reset mode */
746
    if (fdctrl->state & FD_CTRL_RESET) {
B
bellard 已提交
747
            FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
B
bellard 已提交
748 749 750 751 752
            return;
        }
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
    if (value & 0x80) {
753 754 755
        fdctrl->state |= FD_CTRL_RESET;
        fdctrl_reset(fdctrl, 1);
        fdctrl->state &= ~FD_CTRL_RESET;
B
bellard 已提交
756 757
    }
    if (value & 0x40) {
758 759
        fdctrl->state |= FD_CTRL_SLEEP;
        fdctrl_reset(fdctrl, 1);
B
bellard 已提交
760 761 762 763 764
    }
//        fdctrl.precomp = (value >> 2) & 0x07;
}

/* Digital input register : 0x07 (read-only) */
765
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
B
bellard 已提交
766 767 768
{
    uint32_t retval = 0;

769 770
    if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
	drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
B
bellard 已提交
771 772
        retval |= 0x80;
    if (retval != 0)
773 774 775
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
    drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
    drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
B
bellard 已提交
776 777 778 779 780

    return retval;
}

/* FIFO state control */
781
static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
B
bellard 已提交
782
{
783 784 785
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
    FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD);
B
bellard 已提交
786 787 788
}

/* Set FIFO status for the host to read */
789
static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
B
bellard 已提交
790
{
791 792 793 794
    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 已提交
795
    if (do_irq)
796
        fdctrl_raise_irq(fdctrl, 0x00);
B
bellard 已提交
797 798 799
}

/* Set an error: unimplemented/unknown command */
800
static void fdctrl_unimplemented (fdctrl_t *fdctrl)
B
bellard 已提交
801 802
{
#if 0
803 804 805
    fdrive_t *cur_drv;

    cur_drv = get_cur_drv(fdctrl);
806
    fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv;
807 808 809
    fdctrl->fifo[1] = 0x00;
    fdctrl->fifo[2] = 0x00;
    fdctrl_set_fifo(fdctrl, 3, 1);
B
bellard 已提交
810
#else
811 812 813
    //    fdctrl_reset_fifo(fdctrl);
    fdctrl->fifo[0] = 0x80;
    fdctrl_set_fifo(fdctrl, 1, 0);
B
bellard 已提交
814 815 816 817
#endif
}

/* Callback for transfer end (stop or abort) */
818 819
static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
				  uint8_t status1, uint8_t status2)
B
bellard 已提交
820
{
821
    fdrive_t *cur_drv;
B
bellard 已提交
822

823
    cur_drv = get_cur_drv(fdctrl);
B
bellard 已提交
824 825
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
                   status0, status1, status2,
826 827
                   status0 | (cur_drv->head << 2) | fdctrl->cur_drv);
    fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv;
828 829 830 831 832 833 834
    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;
835
    if (fdctrl->state & FD_CTRL_BUSY) {
836
        DMA_release_DREQ(fdctrl->dma_chann);
837 838
        fdctrl->state &= ~FD_CTRL_BUSY;
    }
839
    fdctrl_set_fifo(fdctrl, 7, 1);
B
bellard 已提交
840 841 842
}

/* Prepare a data transfer (either DMA or FIFO) */
843
static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
B
bellard 已提交
844
{
845
    fdrive_t *cur_drv;
B
bellard 已提交
846 847 848
    uint8_t kh, kt, ks;
    int did_seek;

849 850 851 852 853
    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 已提交
854
    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
855
                   fdctrl->cur_drv, kh, kt, ks,
B
bellard 已提交
856 857
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
    did_seek = 0;
858
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
B
bellard 已提交
859 860
    case 2:
        /* sect too big */
861 862 863 864
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
865 866 867
        return;
    case 3:
        /* track too big */
868 869 870 871
        fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
872 873 874
        return;
    case 4:
        /* No seek enabled */
875 876 877 878
        fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
B
bellard 已提交
879 880 881 882 883 884 885 886
        return;
    case 1:
        did_seek = 1;
        break;
    default:
        break;
    }
    /* Set the FIFO state */
887 888 889 890 891 892 893
    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 已提交
894
    if (did_seek)
895 896 897 898 899 900 901 902 903 904 905 906 907
        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;
    }
908
    fdctrl->eot = fdctrl->fifo[6];
909
    if (fdctrl->dma_en) {
B
bellard 已提交
910 911
        int dma_mode;
        /* DMA transfer are enabled. Check if DMA channel is well programmed */
912
        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
B
bellard 已提交
913
        dma_mode = (dma_mode >> 2) & 3;
914 915 916 917
        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 已提交
918 919 920 921 922
        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 */
923
            fdctrl->state |= FD_CTRL_BUSY;
B
bellard 已提交
924
            /* Now, we just have to wait for the DMA controller to
B
bellard 已提交
925 926
             * recall us...
             */
927 928
            DMA_hold_DREQ(fdctrl->dma_chann);
            DMA_schedule(fdctrl->dma_chann);
B
bellard 已提交
929
            return;
930 931
        } else {
	    FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
B
bellard 已提交
932 933 934 935
        }
    }
    FLOPPY_DPRINTF("start non-DMA transfer\n");
    /* IO based transfer: calculate len */
936
    fdctrl_raise_irq(fdctrl, 0x00);
B
bellard 已提交
937 938 939 940 941

    return;
}

/* Prepare a transfer of deleted data */
942
static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
B
bellard 已提交
943 944 945 946
{
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
947
    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
B
bellard 已提交
948 949 950
}

/* handlers for DMA transfers */
B
bellard 已提交
951 952
static int fdctrl_transfer_handler (void *opaque, int nchan,
                                    int dma_pos, int dma_len)
B
bellard 已提交
953
{
954 955 956
    fdctrl_t *fdctrl;
    fdrive_t *cur_drv;
    int len, start_pos, rel_pos;
B
bellard 已提交
957 958
    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;

959 960
    fdctrl = opaque;
    if (!(fdctrl->state & FD_CTRL_BUSY)) {
B
bellard 已提交
961 962 963
        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
        return 0;
    }
964 965 966
    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 已提交
967
        status2 = 0x04;
B
bellard 已提交
968 969
    if (dma_len > fdctrl->data_len)
        dma_len = fdctrl->data_len;
970
    if (cur_drv->bs == NULL) {
971 972 973 974 975
	if (fdctrl->data_dir == FD_DIR_WRITE)
	    fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
	else
	    fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
	len = 0;
976 977
        goto transfer_error;
    }
978
    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
B
bellard 已提交
979 980
    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
        len = dma_len - fdctrl->data_pos;
981 982 983 984 985 986 987 988 989 990 991 992
        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 已提交
993 994 995
                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
                               fd_sector(cur_drv));
                /* Sure, image size is too small... */
996
                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
B
bellard 已提交
997
            }
998
        }
999 1000 1001
	switch (fdctrl->data_dir) {
	case FD_DIR_READ:
	    /* READ commands */
B
bellard 已提交
1002 1003 1004 1005
            DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
                              fdctrl->data_pos, len);
/* 	    cpu_physical_memory_write(addr + fdctrl->data_pos, */
/* 				      fdctrl->fifo + rel_pos, len); */
1006 1007 1008
	    break;
	case FD_DIR_WRITE:
            /* WRITE commands */
B
bellard 已提交
1009 1010 1011 1012
            DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
                             fdctrl->data_pos, len);
/*             cpu_physical_memory_read(addr + fdctrl->data_pos, */
/* 				     fdctrl->fifo + rel_pos, len); */
1013 1014 1015 1016 1017
            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;
1018
            }
1019 1020 1021 1022 1023 1024
	    break;
	default:
	    /* SCAN commands */
            {
		uint8_t tmpbuf[FD_SECTOR_LEN];
                int ret;
B
bellard 已提交
1025 1026 1027
                DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
/*                 cpu_physical_memory_read(addr + fdctrl->data_pos, */
/*                                          tmpbuf, len); */
1028
                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
B
bellard 已提交
1029 1030 1031 1032
                if (ret == 0) {
                    status2 = 0x08;
                    goto end_transfer;
                }
1033 1034
                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
B
bellard 已提交
1035 1036 1037 1038
                    status2 = 0x00;
                    goto end_transfer;
                }
            }
1039
	    break;
B
bellard 已提交
1040
        }
1041 1042 1043
	fdctrl->data_pos += len;
	rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
        if (rel_pos == 0) {
B
bellard 已提交
1044
            /* Seek to next sector */
1045 1046 1047 1048
	    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);
1049 1050 1051 1052
            /* 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) {
1053 1054 1055 1056
		cur_drv->sect = 1;
		if (FD_MULTI_TRACK(fdctrl->data_state)) {
		    if (cur_drv->head == 0 &&
			(cur_drv->flags & FDISK_DBL_SIDES) != 0) {	
1057 1058 1059
                        cur_drv->head = 1;
                    } else {
                        cur_drv->head = 0;
1060 1061 1062
			cur_drv->track++;
			if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
			    break;
1063 1064 1065 1066
                    }
                } else {
                    cur_drv->track++;
                    break;
B
bellard 已提交
1067
                }
1068 1069 1070
		FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
			       cur_drv->head, cur_drv->track,
			       cur_drv->sect, fd_sector(cur_drv));
1071 1072
            } else {
                cur_drv->sect++;
B
bellard 已提交
1073 1074 1075 1076
            }
        }
    }
end_transfer:
1077 1078 1079 1080 1081 1082
    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 已提交
1083
        status2 = 0x08;
1084
    if (FD_DID_SEEK(fdctrl->data_state))
B
bellard 已提交
1085
        status0 |= 0x20;
1086 1087
    fdctrl->data_len -= len;
    //    if (fdctrl->data_len == 0)
1088
    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
B
bellard 已提交
1089 1090
transfer_error:

1091
    return len;
B
bellard 已提交
1092 1093 1094
}

/* Data register : 0x05 */
1095
static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
B
bellard 已提交
1096
{
1097
    fdrive_t *cur_drv;
B
bellard 已提交
1098 1099 1100
    uint32_t retval = 0;
    int pos, len;

1101 1102 1103
    cur_drv = get_cur_drv(fdctrl);
    fdctrl->state &= ~FD_CTRL_SLEEP;
    if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) {
B
bellard 已提交
1104 1105 1106
        FLOPPY_ERROR("can't read data in CMD state\n");
        return 0;
    }
1107 1108
    pos = fdctrl->data_pos;
    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
B
bellard 已提交
1109 1110
        pos %= FD_SECTOR_LEN;
        if (pos == 0) {
1111
            len = fdctrl->data_len - fdctrl->data_pos;
B
bellard 已提交
1112 1113 1114
            if (len > FD_SECTOR_LEN)
                len = FD_SECTOR_LEN;
            bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1115
                      fdctrl->fifo, len);
B
bellard 已提交
1116 1117
        }
    }
1118 1119 1120
    retval = fdctrl->fifo[pos];
    if (++fdctrl->data_pos == fdctrl->data_len) {
        fdctrl->data_pos = 0;
1121
        /* Switch from transfer mode to status mode
B
bellard 已提交
1122 1123
         * then from status mode to command mode
         */
1124
        if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1125
            fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1126
        } else {
1127
            fdctrl_reset_fifo(fdctrl);
1128 1129
            fdctrl_reset_irq(fdctrl);
        }
B
bellard 已提交
1130 1131 1132 1133 1134 1135
    }
    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);

    return retval;
}

1136
static void fdctrl_format_sector (fdctrl_t *fdctrl)
B
bellard 已提交
1137
{
1138 1139 1140
    fdrive_t *cur_drv;
    uint8_t kh, kt, ks;
    int did_seek;
B
bellard 已提交
1141

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

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