Blame view

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

/********************************************************/
/* 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                               */
52
53
54
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
55
56
57
58
59
60
61
62
63
/* 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            */
64
65
    FDRIVE_DISK_USER  = 0x04, /* User defined geometry  */
    FDRIVE_DISK_NONE  = 0x05, /* No disk                */
66
67
68
69
70
71
72
73
74
} 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;
75
76
77
78
typedef enum fdisk_flags_t {
    FDISK_DBL_SIDES  = 0x01,
} fdisk_flags_t;
79
80
81
82
83
84
85
86
87
88
typedef struct fdrive_t {
    BlockDriverState *bs;
    /* Drive status */
    fdrive_type_t drive;
    uint8_t perpendicular;    /* 2.88 MB access mode    */
    /* Position */
    uint8_t head;
    uint8_t track;
    uint8_t sect;
    /* Media */
89
    fdisk_flags_t flags;
90
91
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
92
    uint16_t bps;             /* Bytes per sector       */
93
94
95
    uint8_t ro;               /* Is read-only           */
} fdrive_t;
96
static void fd_init (fdrive_t *drv, BlockDriverState *bs)
97
98
{
    /* Drive */
99
    drv->bs = bs;
bellard authored
100
    drv->drive = FDRIVE_DRV_NONE;
101
102
    drv->perpendicular = 0;
    /* Disk */
103
    drv->last_sect = 0;
104
105
106
107
    drv->max_track = 0;
}

static int _fd_sector (uint8_t head, uint8_t track,
108
                       uint8_t sect, uint8_t last_sect)
109
110
111
112
113
114
115
116
117
118
{
    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);
}
119
120
121
122
123
124
125
/* Seek to a new position:
 * returns 0 if already on right track
 * returns 1 if track changed
 * returns 2 if track is invalid
 * returns 3 if sector is invalid
 * returns 4 if seek is disabled
 */
126
127
128
129
static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
                    int enable_seek)
{
    uint32_t sector;
130
131
132
    int ret;

    if (track > drv->max_track ||
133
        (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
134
135
136
137
        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);
138
139
140
        return 2;
    }
    if (sect > drv->last_sect) {
141
142
143
144
        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);
145
146
147
        return 3;
    }
    sector = _fd_sector(head, track, sect, drv->last_sect);
148
    ret = 0;
149
150
151
152
153
154
155
156
157
    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;
158
159
        if (drv->track != track)
            ret = 1;
160
161
162
163
        drv->track = track;
        drv->sect = sect;
    }
164
    return ret;
165
166
167
168
169
170
171
172
173
174
175
}

/* 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;
}
176
177
178
179
180
181
182
/* 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;
183
    const char *str;
184
185
} fd_format_t;
blueswir1 authored
186
static const fd_format_t fd_formats[] = {
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
    /* 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", },
224
    /* 320 kB 5"1/4 floppy disks */
225
226
227
228
229
230
231
232
    { 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, },
};
233
/* Revalidate a disk drive after a disk change */
234
static void fd_revalidate (fdrive_t *drv)
235
{
blueswir1 authored
236
    const fd_format_t *parse;
237
    uint64_t nb_sectors, size;
238
    int i, first_match, match;
239
    int nb_heads, max_track, last_sect, ro;
240
241

    FLOPPY_DPRINTF("revalidate\n");
242
    if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
243
244
245
246
        ro = bdrv_is_read_only(drv->bs);
        bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
        if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
            FLOPPY_DPRINTF("User defined disk (%d %d %d)",
247
                           nb_heads - 1, max_track, last_sect);
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
273
274
275
276
277
278
279
        } 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;
            FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
280
                           nb_heads, max_track, last_sect, ro ? "ro" : "rw");
281
282
283
284
285
286
287
288
289
        }
        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;
290
    } else {
291
        FLOPPY_DPRINTF("No disk in drive\n");
292
        drv->last_sect = 0;
293
294
        drv->max_track = 0;
        drv->flags &= ~FDISK_DBL_SIDES;
295
    }
296
297
}
298
/********************************************************/
bellard authored
299
/* Intel 82078 floppy disk controller emulation          */
300
301
302
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
bellard authored
303
304
static int fdctrl_transfer_handler (void *opaque, int nchan,
                                    int dma_pos, int dma_len);
305
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status0);
306
307
static uint32_t fdctrl_read_statusA (fdctrl_t *fdctrl);
308
309
310
311
312
313
314
315
316
317
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);
318
319
320
321
322
323
324
325
326
327

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

enum {
328
329
330
    FD_STATE_MULTI  = 0x01,	/* multi track flag */
    FD_STATE_FORMAT = 0x02,	/* format flag */
    FD_STATE_SEEK   = 0x04,	/* seek flag */
331
332
};
333
enum {
334
335
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
336
337
338
339
340
341
342
343
344
    FD_REG_DOR = 0x02,
    FD_REG_TDR = 0x03,
    FD_REG_MSR = 0x04,
    FD_REG_DSR = 0x04,
    FD_REG_FIFO = 0x05,
    FD_REG_DIR = 0x07,
};

enum {
345
    FD_CMD_READ_TRACK = 0x02,
346
347
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
348
349
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
350
351
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
352
353
354
355
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
356
357
358
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
359
    FD_CMD_SCAN_EQUAL = 0x11,
360
361
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
362
363
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
364
365
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
366
367
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
    FD_CMD_SAVE = 0x2c,
    FD_CMD_OPTION = 0x33,
    FD_CMD_RESTORE = 0x4c,
    FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
    FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
    FD_CMD_FORMAT_AND_WRITE = 0xcd,
    FD_CMD_RELATIVE_SEEK_IN = 0xcf,
};

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

enum {
    FD_SR0_EQPMT    = 0x10,
    FD_SR0_SEEK     = 0x20,
    FD_SR0_ABNTERM  = 0x40,
    FD_SR0_INVCMD   = 0x80,
    FD_SR0_RDYCHG   = 0xc0,
};

enum {
394
395
396
397
398
399
400
401
402
    FD_SR1_EC       = 0x80, /* End of cylinder */
};

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

enum {
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
    FD_SRA_DIR      = 0x01,
    FD_SRA_nWP      = 0x02,
    FD_SRA_nINDX    = 0x04,
    FD_SRA_HDSEL    = 0x08,
    FD_SRA_nTRK0    = 0x10,
    FD_SRA_STEP     = 0x20,
    FD_SRA_nDRV2    = 0x40,
    FD_SRA_INTPEND  = 0x80,
};

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

enum {
423
424
425
#if MAX_FD == 4
    FD_DOR_SELMASK  = 0x03,
#else
426
    FD_DOR_SELMASK  = 0x01,
427
#endif
428
429
430
431
432
433
434
435
436
    FD_DOR_nRESET   = 0x04,
    FD_DOR_DMAEN    = 0x08,
    FD_DOR_MOTEN0   = 0x10,
    FD_DOR_MOTEN1   = 0x20,
    FD_DOR_MOTEN2   = 0x40,
    FD_DOR_MOTEN3   = 0x80,
};

enum {
437
#if MAX_FD == 4
438
    FD_TDR_BOOTSEL  = 0x0c,
439
440
441
#else
    FD_TDR_BOOTSEL  = 0x04,
#endif
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
};

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

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

enum {
    FD_DIR_DSKCHG   = 0x80,
};
465
466
#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
467
#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
468
469
struct fdctrl_t {
bellard authored
470
    /* Controller's identification */
471
472
    uint8_t version;
    /* HW */
pbrook authored
473
    qemu_irq irq;
474
    int dma_chann;
475
    target_phys_addr_t io_base;
bellard authored
476
    /* Controller state */
477
    QEMUTimer *result_timer;
478
479
    uint8_t sra;
    uint8_t srb;
480
    uint8_t dor;
481
    uint8_t tdr;
482
    uint8_t dsr;
483
    uint8_t msr;
484
    uint8_t cur_drv;
485
486
487
    uint8_t status0;
    uint8_t status1;
    uint8_t status2;
488
    /* Command FIFO */
489
    uint8_t *fifo;
490
491
492
493
    uint32_t data_pos;
    uint32_t data_len;
    uint8_t data_state;
    uint8_t data_dir;
494
    uint8_t eot; /* last wanted sector */
495
496
497
498
499
500
501
502
503
504
    /* 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;
505
    /* Sun4m quirks? */
blueswir1 authored
506
    int sun4m;
507
    /* Floppy drives */
508
    fdrive_t drives[MAX_FD];
509
510
511
512
513
514
515
};

static uint32_t fdctrl_read (void *opaque, uint32_t reg)
{
    fdctrl_t *fdctrl = opaque;
    uint32_t retval;
516
    switch (reg & 0x07) {
517
518
    case FD_REG_SRA:
        retval = fdctrl_read_statusA(fdctrl);
519
        break;
520
    case FD_REG_SRB:
521
522
        retval = fdctrl_read_statusB(fdctrl);
        break;
523
    case FD_REG_DOR:
524
525
        retval = fdctrl_read_dor(fdctrl);
        break;
526
    case FD_REG_TDR:
527
        retval = fdctrl_read_tape(fdctrl);
528
        break;
529
    case FD_REG_MSR:
530
        retval = fdctrl_read_main_status(fdctrl);
531
        break;
532
    case FD_REG_FIFO:
533
        retval = fdctrl_read_data(fdctrl);
534
        break;
535
    case FD_REG_DIR:
536
        retval = fdctrl_read_dir(fdctrl);
537
        break;
538
    default:
539
540
        retval = (uint32_t)(-1);
        break;
541
    }
542
    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
543
544
545
546
547
548
549
550

    return retval;
}

static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    fdctrl_t *fdctrl = opaque;
551
552
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
553
    switch (reg & 0x07) {
554
    case FD_REG_DOR:
555
556
        fdctrl_write_dor(fdctrl, value);
        break;
557
    case FD_REG_TDR:
558
        fdctrl_write_tape(fdctrl, value);
559
        break;
560
    case FD_REG_DSR:
561
        fdctrl_write_rate(fdctrl, value);
562
        break;
563
    case FD_REG_FIFO:
564
        fdctrl_write_data(fdctrl, value);
565
        break;
566
    default:
567
        break;
568
    }
569
570
}
571
572
static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
{
573
    return fdctrl_read(opaque, (uint32_t)reg);
574
575
}
576
static void fdctrl_write_mem (void *opaque,
577
578
                              target_phys_addr_t reg, uint32_t value)
{
579
    fdctrl_write(opaque, (uint32_t)reg, value);
580
581
}
bellard authored
582
static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
583
584
585
    fdctrl_read_mem,
    fdctrl_read_mem,
    fdctrl_read_mem,
bellard authored
586
587
588
};

static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
589
590
591
    fdctrl_write_mem,
    fdctrl_write_mem,
    fdctrl_write_mem,
bellard authored
592
593
};
594
595
596
597
598
599
600
601
602
603
604
605
static CPUReadMemoryFunc *fdctrl_mem_read_strict[3] = {
    fdctrl_read_mem,
    NULL,
    NULL,
};

static CPUWriteMemoryFunc *fdctrl_mem_write_strict[3] = {
    fdctrl_write_mem,
    NULL,
    NULL,
};
606
607
608
609
610
611
612
613
614
615
static void fd_save (QEMUFile *f, fdrive_t *fd)
{
    qemu_put_8s(f, &fd->head);
    qemu_put_8s(f, &fd->track);
    qemu_put_8s(f, &fd->sect);
}

static void fdc_save (QEMUFile *f, void *opaque)
{
    fdctrl_t *s = opaque;
616
617
    uint8_t tmp;
    int i;
618
    uint8_t dor = s->dor | GET_CUR_DRV(s);
619
620
621
622
    /* Controller state */
    qemu_put_8s(f, &s->sra);
    qemu_put_8s(f, &s->srb);
623
    qemu_put_8s(f, &dor);
624
    qemu_put_8s(f, &s->tdr);
625
626
627
628
629
630
    qemu_put_8s(f, &s->dsr);
    qemu_put_8s(f, &s->msr);
    qemu_put_8s(f, &s->status0);
    qemu_put_8s(f, &s->status1);
    qemu_put_8s(f, &s->status2);
    /* Command FIFO */
631
632
633
634
635
636
    qemu_put_buffer(f, s->fifo, FD_SECTOR_LEN);
    qemu_put_be32s(f, &s->data_pos);
    qemu_put_be32s(f, &s->data_len);
    qemu_put_8s(f, &s->data_state);
    qemu_put_8s(f, &s->data_dir);
    qemu_put_8s(f, &s->eot);
637
    /* States kept only to be returned back */
638
639
640
641
642
643
    qemu_put_8s(f, &s->timer0);
    qemu_put_8s(f, &s->timer1);
    qemu_put_8s(f, &s->precomp_trk);
    qemu_put_8s(f, &s->config);
    qemu_put_8s(f, &s->lock);
    qemu_put_8s(f, &s->pwrd);
644
645
646
647
648

    tmp = MAX_FD;
    qemu_put_8s(f, &tmp);
    for (i = 0; i < MAX_FD; i++)
        fd_save(f, &s->drives[i]);
649
650
651
652
653
654
655
656
657
658
659
660
661
662
}

static int fd_load (QEMUFile *f, fdrive_t *fd)
{
    qemu_get_8s(f, &fd->head);
    qemu_get_8s(f, &fd->track);
    qemu_get_8s(f, &fd->sect);

    return 0;
}

static int fdc_load (QEMUFile *f, void *opaque, int version_id)
{
    fdctrl_t *s = opaque;
663
664
    int i, ret = 0;
    uint8_t n;
665
666
    if (version_id != 2)
667
668
        return -EINVAL;
669
670
671
    /* Controller state */
    qemu_get_8s(f, &s->sra);
    qemu_get_8s(f, &s->srb);
672
673
674
    qemu_get_8s(f, &s->dor);
    SET_CUR_DRV(s, s->dor & FD_DOR_SELMASK);
    s->dor &= ~FD_DOR_SELMASK;
675
    qemu_get_8s(f, &s->tdr);
676
677
678
679
680
681
    qemu_get_8s(f, &s->dsr);
    qemu_get_8s(f, &s->msr);
    qemu_get_8s(f, &s->status0);
    qemu_get_8s(f, &s->status1);
    qemu_get_8s(f, &s->status2);
    /* Command FIFO */
682
683
684
685
686
687
    qemu_get_buffer(f, s->fifo, FD_SECTOR_LEN);
    qemu_get_be32s(f, &s->data_pos);
    qemu_get_be32s(f, &s->data_len);
    qemu_get_8s(f, &s->data_state);
    qemu_get_8s(f, &s->data_dir);
    qemu_get_8s(f, &s->eot);
688
    /* States kept only to be returned back */
689
690
691
692
693
694
    qemu_get_8s(f, &s->timer0);
    qemu_get_8s(f, &s->timer1);
    qemu_get_8s(f, &s->precomp_trk);
    qemu_get_8s(f, &s->config);
    qemu_get_8s(f, &s->lock);
    qemu_get_8s(f, &s->pwrd);
695
    qemu_get_8s(f, &n);
696
697
698
699
700
701
702
703
704
    if (n > MAX_FD)
        return -EINVAL;

    for (i = 0; i < n; i++) {
        ret = fd_load(f, &s->drives[i]);
        if (ret != 0)
            break;
    }
705
706
707
708
709
710
711
712
713
714
715

    return ret;
}

static void fdctrl_external_reset(void *opaque)
{
    fdctrl_t *s = opaque;

    fdctrl_reset(s, 0);
}
716
717
718
719
720
721
722
723
724
725
static void fdctrl_handle_tc(void *opaque, int irq, int level)
{
    //fdctrl_t *s = opaque;

    if (level) {
        // XXX
        FLOPPY_DPRINTF("TC pulsed\n");
    }
}
726
727
/* XXX: may change if moved to bdrv */
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
728
{
729
    return fdctrl->drives[drive_num].drive;
730
731
732
}

/* Change IRQ state */
733
static void fdctrl_reset_irq (fdctrl_t *fdctrl)
734
{
735
736
    if (!(fdctrl->sra & FD_SRA_INTPEND))
        return;
737
    FLOPPY_DPRINTF("Reset interrupt\n");
pbrook authored
738
    qemu_set_irq(fdctrl->irq, 0);
739
    fdctrl->sra &= ~FD_SRA_INTPEND;
740
741
}
742
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status0)
743
{
744
745
746
747
748
    /* Sparc mutation */
    if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
        /* XXX: not sure */
        fdctrl->msr &= ~FD_MSR_CMDBUSY;
        fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
749
        fdctrl->status0 = status0;
750
        return;
bellard authored
751
    }
752
    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
pbrook authored
753
        qemu_set_irq(fdctrl->irq, 1);
754
        fdctrl->sra |= FD_SRA_INTPEND;
755
    }
756
757
    fdctrl->status0 = status0;
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
758
759
}
bellard authored
760
/* Reset controller */
761
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
762
763
764
{
    int i;
bellard authored
765
    FLOPPY_DPRINTF("reset controller\n");
766
    fdctrl_reset_irq(fdctrl);
bellard authored
767
    /* Initialise controller */
768
769
770
771
    fdctrl->sra = 0;
    fdctrl->srb = 0xc0;
    if (!fdctrl->drives[1].bs)
        fdctrl->sra |= FD_SRA_nDRV2;
772
    fdctrl->cur_drv = 0;
773
    fdctrl->dor = FD_DOR_nRESET;
774
    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
775
    fdctrl->msr = FD_MSR_RQM;
776
    /* FIFO state */
777
778
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
779
    fdctrl->data_state = 0;
780
    fdctrl->data_dir = FD_DIR_WRITE;
781
    for (i = 0; i < MAX_FD; i++)
782
        fd_recalibrate(&fdctrl->drives[i]);
783
    fdctrl_reset_fifo(fdctrl);
784
    if (do_irq) {
785
        fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
786
    }
787
788
789
790
}

static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
{
791
    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
792
793
794
795
}

static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
{
796
797
798
799
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
        return &fdctrl->drives[1];
    else
        return &fdctrl->drives[0];
800
801
}
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
#if MAX_FD == 4
static inline fdrive_t *drv2 (fdctrl_t *fdctrl)
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
        return &fdctrl->drives[2];
    else
        return &fdctrl->drives[1];
}

static inline fdrive_t *drv3 (fdctrl_t *fdctrl)
{
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
        return &fdctrl->drives[3];
    else
        return &fdctrl->drives[2];
}
#endif
820
821
static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
{
822
823
824
825
826
827
828
829
830
    switch (fdctrl->cur_drv) {
        case 0: return drv0(fdctrl);
        case 1: return drv1(fdctrl);
#if MAX_FD == 4
        case 2: return drv2(fdctrl);
        case 3: return drv3(fdctrl);
#endif
        default: return NULL;
    }
831
832
}
833
834
835
836
837
838
839
840
841
842
/* Status A register : 0x00 (read-only) */
static uint32_t fdctrl_read_statusA (fdctrl_t *fdctrl)
{
    uint32_t retval = fdctrl->sra;

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

    return retval;
}
843
/* Status B register : 0x01 (read-only) */
844
static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
845
{
846
847
848
849
850
    uint32_t retval = fdctrl->srb;

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

    return retval;
851
852
853
}

/* Digital output register : 0x02 */
854
static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
855
{
856
    uint32_t retval = fdctrl->dor;
857
858

    /* Selected drive */
859
    retval |= fdctrl->cur_drv;
860
861
862
863
864
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}
865
static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
866
867
{
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884

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

    /* Drive */
    if (value & 1)
        fdctrl->srb |= FD_SRB_DR0;
    else
        fdctrl->srb &= ~FD_SRB_DR0;
885
    /* Reset */
886
    if (!(value & FD_DOR_nRESET)) {
887
        if (fdctrl->dor & FD_DOR_nRESET) {
bellard authored
888
            FLOPPY_DPRINTF("controller enter RESET state\n");
889
890
        }
    } else {
891
        if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
892
            FLOPPY_DPRINTF("controller out of RESET state\n");
893
            fdctrl_reset(fdctrl, 1);
894
            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
895
896
897
        }
    }
    /* Selected drive */
898
    fdctrl->cur_drv = value & FD_DOR_SELMASK;
899
900

    fdctrl->dor = value;
901
902
903
}

/* Tape drive register : 0x03 */
904
static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
905
{
906
    uint32_t retval = fdctrl->tdr;
907
908
909
910
911
912

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

    return retval;
}
913
static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
914
915
{
    /* Reset mode */
916
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
917
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
918
919
920
921
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
922
    fdctrl->tdr = value & FD_TDR_BOOTSEL;
923
924
925
926
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
927
static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
928
{
929
    uint32_t retval = fdctrl->msr;
930
931
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
932
    fdctrl->dor |= FD_DOR_nRESET;
933
934
935
936
937
938
939
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
940
static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
941
942
{
    /* Reset mode */
943
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
944
945
946
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
947
948
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
949
    if (value & FD_DSR_SWRESET) {
950
        fdctrl->dor &= ~FD_DOR_nRESET;
951
        fdctrl_reset(fdctrl, 1);
952
        fdctrl->dor |= FD_DOR_nRESET;
953
    }
954
    if (value & FD_DSR_PWRDOWN) {
955
        fdctrl_reset(fdctrl, 1);
956
    }
957
    fdctrl->dsr = value;
958
959
}
bellard authored
960
961
962
static int fdctrl_media_changed(fdrive_t *drv)
{
    int ret;
963
964
    if (!drv->bs)
bellard authored
965
966
967
968
969
970
971
972
        return 0;
    ret = bdrv_media_changed(drv->bs);
    if (ret) {
        fd_revalidate(drv);
    }
    return ret;
}
973
/* Digital input register : 0x07 (read-only) */
974
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
975
976
977
{
    uint32_t retval = 0;
978
979
980
981
982
983
984
    if (fdctrl_media_changed(drv0(fdctrl))
     || fdctrl_media_changed(drv1(fdctrl))
#if MAX_FD == 4
     || fdctrl_media_changed(drv2(fdctrl))
     || fdctrl_media_changed(drv3(fdctrl))
#endif
        )
985
        retval |= FD_DIR_DSKCHG;
986
    if (retval != 0)
987
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
988
989
990
991
992

    return retval;
}

/* FIFO state control */
993
static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
994
{
995
996
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
997
    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
998
999
1000
}

/* Set FIFO status for the host to read */
1001
static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
1002
{
1003
1004
1005
    fdctrl->data_dir = FD_DIR_READ;
    fdctrl->data_len = fifo_len;
    fdctrl->data_pos = 0;
1006
    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
1007
    if (do_irq)
1008
        fdctrl_raise_irq(fdctrl, 0x00);
1009
1010
1011
}

/* Set an error: unimplemented/unknown command */
1012
static void fdctrl_unimplemented (fdctrl_t *fdctrl, int direction)
1013
{
1014
    FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]);
1015
    fdctrl->fifo[0] = FD_SR0_INVCMD;
1016
    fdctrl_set_fifo(fdctrl, 1, 0);
1017
1018
}
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
/* Seek to next sector */
static int fdctrl_seek_to_next_sect (fdctrl_t *fdctrl, fdrive_t *cur_drv)
{
    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
                   cur_drv->head, cur_drv->track, cur_drv->sect,
                   fd_sector(cur_drv));
    /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
       error in fact */
    if (cur_drv->sect >= cur_drv->last_sect ||
        cur_drv->sect == fdctrl->eot) {
        cur_drv->sect = 1;
        if (FD_MULTI_TRACK(fdctrl->data_state)) {
            if (cur_drv->head == 0 &&
                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
                cur_drv->head = 1;
            } else {
                cur_drv->head = 0;
                cur_drv->track++;
                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
                    return 0;
            }
        } else {
            cur_drv->track++;
            return 0;
        }
        FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
                       cur_drv->head, cur_drv->track,
                       cur_drv->sect, fd_sector(cur_drv));
    } else {
        cur_drv->sect++;
    }
    return 1;
}
1053
/* Callback for transfer end (stop or abort) */
1054
static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
1055
                                  uint8_t status1, uint8_t status2)
1056
{
1057
    fdrive_t *cur_drv;
1058
1059
    cur_drv = get_cur_drv(fdctrl);
1060
1061
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
                   status0, status1, status2,
1062
1063
                   status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl));
    fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1064
1065
1066
1067
1068
1069
1070
    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;
1071
    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1072
        DMA_release_DREQ(fdctrl->dma_chann);
1073
    }
1074
    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
1075
    fdctrl->msr &= ~FD_MSR_NONDMA;
1076
    fdctrl_set_fifo(fdctrl, 7, 1);
1077
1078
1079
}

/* Prepare a data transfer (either DMA or FIFO) */
1080
static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
1081
{
1082
    fdrive_t *cur_drv;
1083
    uint8_t kh, kt, ks;
1084
    int did_seek = 0;
1085
1086
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1087
1088
1089
1090
    cur_drv = get_cur_drv(fdctrl);
    kt = fdctrl->fifo[2];
    kh = fdctrl->fifo[3];
    ks = fdctrl->fifo[4];
bellard authored
1091
    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
1092
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1093
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
1094
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1095
1096
    case 2:
        /* sect too big */
1097
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1098
1099
1100
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
1101
1102
1103
        return;
    case 3:
        /* track too big */
1104
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1105
1106
1107
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
1108
1109
1110
        return;
    case 4:
        /* No seek enabled */
1111
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1112
1113
1114
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
1115
1116
1117
1118
1119
1120
1121
        return;
    case 1:
        did_seek = 1;
        break;
    default:
        break;
    }
1122
1123
    /* Set the FIFO state */
1124
1125
    fdctrl->data_dir = direction;
    fdctrl->data_pos = 0;
1126
    fdctrl->msr |= FD_MSR_CMDBUSY;
1127
1128
1129
1130
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
1131
    if (did_seek)
1132
1133
1134
1135
1136
1137
        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 {
1138
        int tmp;
1139
        fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
1140
        tmp = (fdctrl->fifo[6] - ks + 1);
1141
        if (fdctrl->fifo[0] & 0x80)
1142
            tmp += fdctrl->fifo[6];
1143
        fdctrl->data_len *= tmp;
1144
    }
1145
    fdctrl->eot = fdctrl->fifo[6];
1146
    if (fdctrl->dor & FD_DOR_DMAEN) {
1147
1148
        int dma_mode;
        /* DMA transfer are enabled. Check if DMA channel is well programmed */
1149
        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
1150
        dma_mode = (dma_mode >> 2) & 3;
1151
        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
1152
                       dma_mode, direction,
1153
                       (128 << fdctrl->fifo[5]) *
1154
                       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
1155
1156
1157
1158
1159
        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 */
1160
            fdctrl->msr &= ~FD_MSR_RQM;
bellard authored
1161
            /* Now, we just have to wait for the DMA controller to
1162
1163
             * recall us...
             */
1164
1165
            DMA_hold_DREQ(fdctrl->dma_chann);
            DMA_schedule(fdctrl->dma_chann);
1166
            return;
1167
        } else {
1168
            FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
1169
1170
1171
        }
    }
    FLOPPY_DPRINTF("start non-DMA transfer\n");
1172
    fdctrl->msr |= FD_MSR_NONDMA;
1173
1174
    if (direction != FD_DIR_WRITE)
        fdctrl->msr |= FD_MSR_DIO;
1175
    /* IO based transfer: calculate len */
1176
    fdctrl_raise_irq(fdctrl, 0x00);
1177
1178
1179
1180
1181

    return;
}

/* Prepare a transfer of deleted data */
1182
static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
1183
{
1184
1185
    FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n");
1186
1187
1188
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
1189
    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1190
1191
1192
}

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

/* Data register : 0x05 */
1303
static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
1304
{
1305
    fdrive_t *cur_drv;
1306
    uint32_t retval = 0;
1307
    int pos;
1308
1309
    cur_drv = get_cur_drv(fdctrl);
1310
1311
1312
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
    if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
        FLOPPY_ERROR("controller not ready for reading\n");
1313
1314
        return 0;
    }
1315
    pos = fdctrl->data_pos;
1316
    if (fdctrl->msr & FD_MSR_NONDMA) {
1317
1318
        pos %= FD_SECTOR_LEN;
        if (pos == 0) {
1319
1320
1321
1322
1323
1324
            if (fdctrl->data_pos != 0)
                if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                    FLOPPY_DPRINTF("error seeking to next sector %d\n",
                                   fd_sector(cur_drv));
                    return 0;
                }
1325
1326
1327
1328
1329
1330
            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
                FLOPPY_DPRINTF("error getting sector %d\n",
                               fd_sector(cur_drv));
                /* Sure, image size is too small... */
                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
            }
1331
1332
        }
    }
1333
1334
1335
    retval = fdctrl->fifo[pos];
    if (++fdctrl->data_pos == fdctrl->data_len) {
        fdctrl->data_pos = 0;
1336
        /* Switch from transfer mode to status mode
1337
1338
         * then from status mode to command mode
         */
1339
        if (fdctrl->msr & FD_MSR_NONDMA) {
1340
            fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1341
        } else {
1342
            fdctrl_reset_fifo(fdctrl);
1343
1344
            fdctrl_reset_irq(fdctrl);
        }
1345
1346
1347
1348
1349
1350
    }
    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);

    return retval;
}
1351
static void fdctrl_format_sector (fdctrl_t *fdctrl)
1352
{
1353
1354
    fdrive_t *cur_drv;
    uint8_t kh, kt, ks;
1355
1356
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1357
1358
1359
1360
1361
    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",
1362
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1363
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
1364
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1365
1366
    case 2:
        /* sect too big */
1367
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1368
1369
1370
1371
1372
1373
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
1374
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1375
1376
1377
1378
1379
1380
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
1381
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 1:
        fdctrl->data_state |= FD_STATE_SEEK;
        break;
    default:
        break;
    }
    memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
    if (cur_drv->bs == NULL ||
        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
ths authored
1395
        FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
1396
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1397
    } else {
1398
1399
1400
1401
        if (cur_drv->sect == cur_drv->last_sect) {
            fdctrl->data_state &= ~FD_STATE_FORMAT;
            /* Last sector done */
            if (FD_DID_SEEK(fdctrl->data_state))
1402
                fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1403
1404
1405
1406
1407
1408
1409
            else
                fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        } else {
            /* More to do */
            fdctrl->data_pos = 0;
            fdctrl->data_len = 4;
        }
1410
1411
1412
    }
}
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
static void fdctrl_handle_lock (fdctrl_t *fdctrl, int direction)
{
    fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
    fdctrl->fifo[0] = fdctrl->lock << 4;
    fdctrl_set_fifo(fdctrl, 1, fdctrl->lock);
}

static void fdctrl_handle_dumpreg (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    /* Drives position */
    fdctrl->fifo[0] = drv0(fdctrl)->track;
    fdctrl->fifo[1] = drv1(fdctrl)->track;
1427
1428
1429
1430
#if MAX_FD == 4
    fdctrl->fifo[2] = drv2(fdctrl)->track;
    fdctrl->fifo[3] = drv3(fdctrl)->track;
#else
1431
1432
    fdctrl->fifo[2] = 0;
    fdctrl->fifo[3] = 0;
1433
#endif
1434
1435
    /* timers */
    fdctrl->fifo[4] = fdctrl->timer0;
1436
    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
    fdctrl->fifo[6] = cur_drv->last_sect;
    fdctrl->fifo[7] = (fdctrl->lock << 7) |
        (cur_drv->perpendicular << 2);
    fdctrl->fifo[8] = fdctrl->config;
    fdctrl->fifo[9] = fdctrl->precomp_trk;
    fdctrl_set_fifo(fdctrl, 10, 0);
}

static void fdctrl_handle_version (fdctrl_t *fdctrl, int direction)
{
    /* Controller's version */
    fdctrl->fifo[0] = fdctrl->version;
    fdctrl_set_fifo(fdctrl, 1, 1);
}

static void fdctrl_handle_partid (fdctrl_t *fdctrl, int direction)
{
    fdctrl->fifo[0] = 0x41; /* Stepping 1 */
    fdctrl_set_fifo(fdctrl, 1, 0);
}

static void fdctrl_handle_restore (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    /* Drives position */
    drv0(fdctrl)->track = fdctrl->fifo[3];
    drv1(fdctrl)->track = fdctrl->fifo[4];
1465
1466
1467
1468
#if MAX_FD == 4
    drv2(fdctrl)->track = fdctrl->fifo[5];
    drv3(fdctrl)->track = fdctrl->fifo[6];
#endif
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
    /* timers */
    fdctrl->timer0 = fdctrl->fifo[7];
    fdctrl->timer1 = fdctrl->fifo[8];
    cur_drv->last_sect = fdctrl->fifo[9];
    fdctrl->lock = fdctrl->fifo[10] >> 7;
    cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
    fdctrl->config = fdctrl->fifo[11];
    fdctrl->precomp_trk = fdctrl->fifo[12];
    fdctrl->pwrd = fdctrl->fifo[13];
    fdctrl_reset_fifo(fdctrl);
}

static void fdctrl_handle_save (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    fdctrl->fifo[0] = 0;
    fdctrl->fifo[1] = 0;
    /* Drives position */
    fdctrl->fifo[2] = drv0(fdctrl)->track;
    fdctrl->fifo[3] = drv1(fdctrl)->track;
1490
1491
1492
1493
#if MAX_FD == 4
    fdctrl->fifo[4] = drv2(fdctrl)->track;
    fdctrl->fifo[5] = drv3(fdctrl)->track;
#else
1494
1495
    fdctrl->fifo[4] = 0;
    fdctrl->fifo[5] = 0;
1496
#endif
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
    /* timers */
    fdctrl->fifo[6] = fdctrl->timer0;
    fdctrl->fifo[7] = fdctrl->timer1;
    fdctrl->fifo[8] = cur_drv->last_sect;
    fdctrl->fifo[9] = (fdctrl->lock << 7) |
        (cur_drv->perpendicular << 2);
    fdctrl->fifo[10] = fdctrl->config;
    fdctrl->fifo[11] = fdctrl->precomp_trk;
    fdctrl->fifo[12] = fdctrl->pwrd;
    fdctrl->fifo[13] = 0;
    fdctrl->fifo[14] = 0;
    fdctrl_set_fifo(fdctrl, 15, 1);
}

static void fdctrl_handle_readid (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    /* XXX: should set main status register to busy */
    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
    qemu_mod_timer(fdctrl->result_timer,
                   qemu_get_clock(vm_clock) + (ticks_per_sec / 50));
}

static void fdctrl_handle_format_track (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1525
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
    cur_drv = get_cur_drv(fdctrl);
    fdctrl->data_state |= FD_STATE_FORMAT;
    if (fdctrl->fifo[0] & 0x80)
        fdctrl->data_state |= FD_STATE_MULTI;
    else
        fdctrl->data_state &= ~FD_STATE_MULTI;
    fdctrl->data_state &= ~FD_STATE_SEEK;
    cur_drv->bps =
        fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
#if 0
    cur_drv->last_sect =
        cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
        fdctrl->fifo[3] / 2;
#else
    cur_drv->last_sect = fdctrl->fifo[3];
#endif
    /* TODO: implement format using DMA expected by the Bochs BIOS
     * and Linux fdformat (read 3 bytes per sector via DMA and fill
     * the sector with the specified fill byte
     */
    fdctrl->data_state &= ~FD_STATE_FORMAT;
    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}

static void fdctrl_handle_specify (fdctrl_t *fdctrl, int direction)
{
    fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
    fdctrl->timer1 = fdctrl->fifo[2] >> 1;
1554
1555
1556
1557
    if (fdctrl->fifo[2] & 1)
        fdctrl->dor &= ~FD_DOR_DMAEN;
    else
        fdctrl->dor |= FD_DOR_DMAEN;
1558
1559
1560
1561
1562
1563
1564
1565
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

static void fdctrl_handle_sense_drive_status (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1566
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1567
1568
1569
1570
1571
1572
    cur_drv = get_cur_drv(fdctrl);
    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
    /* 1 Byte status back */
    fdctrl->fifo[0] = (cur_drv->ro << 6) |
        (cur_drv->track == 0 ? 0x10 : 0x00) |
        (cur_drv->head << 2) |
1573
        GET_CUR_DRV(fdctrl) |
1574
1575
1576
1577
1578
1579
1580
1581
        0x28;
    fdctrl_set_fifo(fdctrl, 1, 0);
}

static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1582
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
    cur_drv = get_cur_drv(fdctrl);
    fd_recalibrate(cur_drv);
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

static void fdctrl_handle_sense_interrupt_status (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

#if 0
    fdctrl->fifo[0] =
1596
        fdctrl->status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1597
#else
1598
    /* XXX: status0 handling is broken for read/write
1599
1600
1601
       commands, so we do this hack. It should be suppressed
       ASAP */
    fdctrl->fifo[0] =
1602
        FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1603
1604
1605
1606
#endif
    fdctrl->fifo[1] = cur_drv->track;
    fdctrl_set_fifo(fdctrl, 2, 0);
    fdctrl_reset_irq(fdctrl);
1607
    fdctrl->status0 = FD_SR0_RDYCHG;
1608
1609
1610
1611
1612
1613
}

static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1614
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
    cur_drv = get_cur_drv(fdctrl);
    fdctrl_reset_fifo(fdctrl);
    if (fdctrl->fifo[2] > cur_drv->max_track) {
        fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
    } else {
        cur_drv->track = fdctrl->fifo[2];
        /* Raise Interrupt */
        fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
    }
}

static void fdctrl_handle_perpendicular_mode (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    if (fdctrl->fifo[1] & 0x80)
        cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
    /* No result back */
1633
    fdctrl_reset_fifo(fdctrl);
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
}

static void fdctrl_handle_configure (fdctrl_t *fdctrl, int direction)
{
    fdctrl->config = fdctrl->fifo[2];
    fdctrl->precomp_trk =  fdctrl->fifo[3];
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

static void fdctrl_handle_powerdown_mode (fdctrl_t *fdctrl, int direction)
{
    fdctrl->pwrd = fdctrl->fifo[1];
    fdctrl->fifo[0] = fdctrl->fifo[1];
    fdctrl_set_fifo(fdctrl, 1, 1);
}

static void fdctrl_handle_option (fdctrl_t *fdctrl, int direction)
{
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

static void fdctrl_handle_drive_specification_command (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv = get_cur_drv(fdctrl);

    if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
        /* Command parameters done */
        if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
            fdctrl->fifo[0] = fdctrl->fifo[1];
            fdctrl->fifo[2] = 0;
            fdctrl->fifo[3] = 0;
            fdctrl_set_fifo(fdctrl, 4, 1);
        } else {
            fdctrl_reset_fifo(fdctrl);
        }
    } else if (fdctrl->data_len > 7) {
        /* ERROR */
        fdctrl->fifo[0] = 0x80 |
1674
            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1675
1676
1677
1678
1679
1680
        fdctrl_set_fifo(fdctrl, 1, 1);
    }
}

static void fdctrl_handle_relative_seek_out (fdctrl_t *fdctrl, int direction)
{
1681
    fdrive_t *cur_drv;
1682
1683
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1684
1685
1686
1687
1688
1689
1690
    cur_drv = get_cur_drv(fdctrl);
    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];
    }
    fdctrl_reset_fifo(fdctrl);
1691
    /* Raise Interrupt */
1692
1693
1694
1695
1696
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

static void fdctrl_handle_relative_seek_in (fdctrl_t *fdctrl, int direction)
{
1697
    fdrive_t *cur_drv;
1698
1699
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
    cur_drv = get_cur_drv(fdctrl);
    if (fdctrl->fifo[2] > cur_drv->track) {
        cur_drv->track = 0;
    } else {
        cur_drv->track -= fdctrl->fifo[2];
    }
    fdctrl_reset_fifo(fdctrl);
    /* Raise Interrupt */
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
static const struct {
    uint8_t value;
    uint8_t mask;
    const char* name;
    int parameters;
    void (*handler)(fdctrl_t *fdctrl, int direction);
    int direction;
} handlers[] = {
    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
};
/* Associate command to an index in the 'handlers' array */
static uint8_t command_to_handler[256];
1755
1756
1757
static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
{
    fdrive_t *cur_drv;
1758
    int pos;
1759
1760
    /* Reset mode */
1761
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
1762
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
1763
1764
        return;
    }
1765
1766
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
        FLOPPY_ERROR("controller not ready for writing\n");
1767
1768
        return;
    }
1769
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
1770
    /* Is it write command time ? */
1771
    if (fdctrl->msr & FD_MSR_NONDMA) {
1772
        /* FIFO data write */
1773
1774
1775
1776
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
1777
            fdctrl->data_pos == fdctrl->data_len) {
1778
1779
1780
1781
1782
            cur_drv = get_cur_drv(fdctrl);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
                FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
                return;
            }
1783
1784
1785
1786
1787
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                FLOPPY_DPRINTF("error seeking to next sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
1788
        }
1789
        /* Switch from transfer mode to status mode
1790
1791
         * then from status mode to command mode
         */
1792
        if (fdctrl->data_pos == fdctrl->data_len)
1793
            fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1794
1795
        return;
    }
1796
    if (fdctrl->data_pos == 0) {
1797
        /* Command */
1798
1799
1800
        pos = command_to_handler[value & 0xff];
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        fdctrl->data_len = handlers[pos].parameters + 1;
1801
    }
1802
1803
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
1804
1805
    fdctrl->fifo[fdctrl->data_pos++] = value;
    if (fdctrl->data_pos == fdctrl->data_len) {
1806
1807
1808
        /* We now have all parameters
         * and will be able to treat the command
         */
1809
1810
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            fdctrl_format_sector(fdctrl);
1811
1812
            return;
        }
1813
1814
1815
1816
        pos = command_to_handler[fdctrl->fifo[0] & 0xff];
        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
1817
1818
    }
}
1819
1820
1821
1822

static void fdctrl_result_timer(void *opaque)
{
    fdctrl_t *fdctrl = opaque;
1823
    fdrive_t *cur_drv = get_cur_drv(fdctrl);
1824
1825
1826
1827
1828
1829
1830
1831
    /* Pretend we are spinning.
     * This is needed for Coherent, which uses READ ID to check for
     * sector interleaving.
     */
    if (cur_drv->last_sect != 0) {
        cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
    }
1832
1833
    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873

/* Init functions */
static fdctrl_t *fdctrl_init_common (qemu_irq irq, int dma_chann,
                                     target_phys_addr_t io_base,
                                     BlockDriverState **fds)
{
    fdctrl_t *fdctrl;
    int i, j;

    /* Fill 'command_to_handler' lookup table */
    for (i = sizeof(handlers)/sizeof(handlers[0]) - 1; i >= 0; i--) {
        for (j = 0; j < sizeof(command_to_handler); j++) {
            if ((j & handlers[i].mask) == handlers[i].value)
                command_to_handler[j] = i;
        }
    }

    FLOPPY_DPRINTF("init controller\n");
    fdctrl = qemu_mallocz(sizeof(fdctrl_t));
    if (!fdctrl)
        return NULL;
    fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
    if (fdctrl->fifo == NULL) {
        qemu_free(fdctrl);
        return NULL;
    }
    fdctrl->result_timer = qemu_new_timer(vm_clock,
                                          fdctrl_result_timer, fdctrl);

    fdctrl->version = 0x90; /* Intel 82078 controller */
    fdctrl->irq = irq;
    fdctrl->dma_chann = dma_chann;
    fdctrl->io_base = io_base;
    fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
    if (fdctrl->dma_chann != -1) {
        DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
    }
    for (i = 0; i < MAX_FD; i++) {
        fd_init(&fdctrl->drives[i], fds[i]);
    }
1874
1875
    fdctrl_external_reset(fdctrl);
    register_savevm("fdc", io_base, 2, fdc_save, fdc_load, fdctrl);
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
    qemu_register_reset(fdctrl_external_reset, fdctrl);
    for (i = 0; i < MAX_FD; i++) {
        fd_revalidate(&fdctrl->drives[i]);
    }

    return fdctrl;
}

fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped,
                       target_phys_addr_t io_base,
                       BlockDriverState **fds)
{
    fdctrl_t *fdctrl;
    int io_mem;

    fdctrl = fdctrl_init_common(irq, dma_chann, io_base, fds);

    fdctrl->sun4m = 0;
    if (mem_mapped) {
        io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write,
                                        fdctrl);
        cpu_register_physical_memory(io_base, 0x08, io_mem);
    } else {
        register_ioport_read((uint32_t)io_base + 0x01, 5, 1, &fdctrl_read,
                             fdctrl);
        register_ioport_read((uint32_t)io_base + 0x07, 1, 1, &fdctrl_read,
                             fdctrl);
        register_ioport_write((uint32_t)io_base + 0x01, 5, 1, &fdctrl_write,
                              fdctrl);
        register_ioport_write((uint32_t)io_base + 0x07, 1, 1, &fdctrl_write,
                              fdctrl);
    }

    return fdctrl;
}

fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
                             BlockDriverState **fds, qemu_irq *fdc_tc)
{
    fdctrl_t *fdctrl;
    int io_mem;
1918
    fdctrl = fdctrl_init_common(irq, -1, io_base, fds);
1919
1920
1921
1922
1923
1924
1925
1926
1927
    fdctrl->sun4m = 1;
    io_mem = cpu_register_io_memory(0, fdctrl_mem_read_strict,
                                    fdctrl_mem_write_strict,
                                    fdctrl);
    cpu_register_physical_memory(io_base, 0x08, io_mem);
    *fdc_tc = *qemu_allocate_irqs(fdctrl_handle_tc, fdctrl, 1);

    return fdctrl;
}