Blame view

hw/fdc.c 60.7 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.
 */
Blue Swirl authored
29
pbrook authored
30
31
32
33
34
#include "hw.h"
#include "fdc.h"
#include "block.h"
#include "qemu-timer.h"
#include "isa.h"
Blue Swirl authored
35
#include "sysbus.h"
Blue Swirl authored
36
#include "qdev-addr.h"
37
38
39
40
41
42

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

#ifdef DEBUG_FLOPPY
43
44
#define FLOPPY_DPRINTF(fmt, ...)                                \
    do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
45
#else
46
#define FLOPPY_DPRINTF(fmt, ...)
47
48
#endif
49
50
#define FLOPPY_ERROR(fmt, ...)                                          \
    do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
51
52
53
54

/********************************************************/
/* Floppy drive emulation                               */
55
56
57
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
58
/* Will always be a fixed parameter for us */
59
60
61
#define FD_SECTOR_LEN          512
#define FD_SECTOR_SC           2   /* Sector size code */
#define FD_RESET_SENSEI_COUNT  4   /* Number of sense interrupts on RESET */
62
63
64
65
66
67

/* 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            */
68
69
    FDRIVE_DISK_USER  = 0x04, /* User defined geometry  */
    FDRIVE_DISK_NONE  = 0x05, /* No disk                */
70
71
72
73
74
75
76
77
78
} 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;
79
80
81
82
typedef enum fdisk_flags_t {
    FDISK_DBL_SIDES  = 0x01,
} fdisk_flags_t;
83
84
85
86
87
88
89
90
91
92
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 */
93
    fdisk_flags_t flags;
94
95
    uint8_t last_sect;        /* Nb sector per track    */
    uint8_t max_track;        /* Nb of tracks           */
96
    uint16_t bps;             /* Bytes per sector       */
97
98
99
    uint8_t ro;               /* Is read-only           */
} fdrive_t;
100
static void fd_init (fdrive_t *drv, BlockDriverState *bs)
101
102
{
    /* Drive */
103
    drv->bs = bs;
bellard authored
104
    drv->drive = FDRIVE_DRV_NONE;
105
106
    drv->perpendicular = 0;
    /* Disk */
107
    drv->last_sect = 0;
108
109
110
111
    drv->max_track = 0;
}

static int _fd_sector (uint8_t head, uint8_t track,
112
                       uint8_t sect, uint8_t last_sect)
113
114
115
116
117
118
119
120
121
122
{
    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);
}
123
124
125
126
127
128
129
/* 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
 */
130
131
132
133
static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
                    int enable_seek)
{
    uint32_t sector;
134
135
136
    int ret;

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

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

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

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

enum {
332
333
334
    FD_STATE_MULTI  = 0x01,	/* multi track flag */
    FD_STATE_FORMAT = 0x02,	/* format flag */
    FD_STATE_SEEK   = 0x04,	/* seek flag */
335
336
};
337
enum {
338
339
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
340
341
342
343
344
345
346
347
348
    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 {
349
    FD_CMD_READ_TRACK = 0x02,
350
351
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
352
353
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
354
355
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
356
357
358
359
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
360
361
362
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
363
    FD_CMD_SCAN_EQUAL = 0x11,
364
365
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
366
367
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
368
369
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
370
371
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    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 {
398
399
400
401
402
403
404
405
406
    FD_SR1_EC       = 0x80, /* End of cylinder */
};

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

enum {
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
    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 {
427
428
429
#if MAX_FD == 4
    FD_DOR_SELMASK  = 0x03,
#else
430
    FD_DOR_SELMASK  = 0x01,
431
#endif
432
433
434
435
436
437
438
439
440
    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 {
441
#if MAX_FD == 4
442
    FD_TDR_BOOTSEL  = 0x0c,
443
444
445
#else
    FD_TDR_BOOTSEL  = 0x04,
#endif
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
};

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

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

    return retval;
}

static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    fdctrl_t *fdctrl = opaque;
556
557
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
blueswir1 authored
558
    switch (reg) {
559
    case FD_REG_DOR:
560
561
        fdctrl_write_dor(fdctrl, value);
        break;
562
    case FD_REG_TDR:
563
        fdctrl_write_tape(fdctrl, value);
564
        break;
565
    case FD_REG_DSR:
566
        fdctrl_write_rate(fdctrl, value);
567
        break;
568
    case FD_REG_FIFO:
569
        fdctrl_write_data(fdctrl, value);
570
        break;
571
    default:
572
        break;
573
    }
574
575
}
blueswir1 authored
576
577
578
579
580
581
582
583
584
585
static uint32_t fdctrl_read_port (void *opaque, uint32_t reg)
{
    return fdctrl_read(opaque, reg & 7);
}

static void fdctrl_write_port (void *opaque, uint32_t reg, uint32_t value)
{
    fdctrl_write(opaque, reg & 7, value);
}
586
587
static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
{
588
    return fdctrl_read(opaque, (uint32_t)reg);
589
590
}
591
static void fdctrl_write_mem (void *opaque,
592
593
                              target_phys_addr_t reg, uint32_t value)
{
594
    fdctrl_write(opaque, (uint32_t)reg, value);
595
596
}
bellard authored
597
static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
598
599
600
    fdctrl_read_mem,
    fdctrl_read_mem,
    fdctrl_read_mem,
bellard authored
601
602
603
};

static CPUWriteMemoryFunc *fdctrl_mem_write[3] = {
604
605
606
    fdctrl_write_mem,
    fdctrl_write_mem,
    fdctrl_write_mem,
bellard authored
607
608
};
609
610
611
612
613
614
615
616
617
618
619
620
static CPUReadMemoryFunc *fdctrl_mem_read_strict[3] = {
    fdctrl_read_mem,
    NULL,
    NULL,
};

static CPUWriteMemoryFunc *fdctrl_mem_write_strict[3] = {
    fdctrl_write_mem,
    NULL,
    NULL,
};
621
622
623
624
625
626
627
628
629
630
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;
631
632
    uint8_t tmp;
    int i;
633
    uint8_t dor = s->dor | GET_CUR_DRV(s);
634
635
636
637
    /* Controller state */
    qemu_put_8s(f, &s->sra);
    qemu_put_8s(f, &s->srb);
638
    qemu_put_8s(f, &dor);
639
    qemu_put_8s(f, &s->tdr);
640
641
642
643
644
645
    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 */
646
647
648
649
650
651
    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);
652
    /* States kept only to be returned back */
653
654
655
656
657
658
    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);
659
660
661
662
663

    tmp = MAX_FD;
    qemu_put_8s(f, &tmp);
    for (i = 0; i < MAX_FD; i++)
        fd_save(f, &s->drives[i]);
664
665
666
667
668
669
670
671
672
673
674
675
676
677
}

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;
678
679
    int i, ret = 0;
    uint8_t n;
680
681
    if (version_id != 2)
682
683
        return -EINVAL;
684
685
686
    /* Controller state */
    qemu_get_8s(f, &s->sra);
    qemu_get_8s(f, &s->srb);
687
688
689
    qemu_get_8s(f, &s->dor);
    SET_CUR_DRV(s, s->dor & FD_DOR_SELMASK);
    s->dor &= ~FD_DOR_SELMASK;
690
    qemu_get_8s(f, &s->tdr);
691
692
693
694
695
696
    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 */
697
698
699
700
701
702
    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);
703
    /* States kept only to be returned back */
704
705
706
707
708
709
    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);
710
    qemu_get_8s(f, &n);
711
712
713
714
715
716
717
718
719
    if (n > MAX_FD)
        return -EINVAL;

    for (i = 0; i < n; i++) {
        ret = fd_load(f, &s->drives[i]);
        if (ret != 0)
            break;
    }
720
721
722
723
724
725
726
727
728
729
730

    return ret;
}

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

    fdctrl_reset(s, 0);
}
731
732
733
734
735
736
737
738
739
740
static void fdctrl_handle_tc(void *opaque, int irq, int level)
{
    //fdctrl_t *s = opaque;

    if (level) {
        // XXX
        FLOPPY_DPRINTF("TC pulsed\n");
    }
}
741
742
/* XXX: may change if moved to bdrv */
int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
743
{
744
    return fdctrl->drives[drive_num].drive;
745
746
747
}

/* Change IRQ state */
748
static void fdctrl_reset_irq (fdctrl_t *fdctrl)
749
{
750
751
    if (!(fdctrl->sra & FD_SRA_INTPEND))
        return;
752
    FLOPPY_DPRINTF("Reset interrupt\n");
pbrook authored
753
    qemu_set_irq(fdctrl->irq, 0);
754
    fdctrl->sra &= ~FD_SRA_INTPEND;
755
756
}
757
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status0)
758
{
759
760
761
762
763
    /* 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;
764
        fdctrl->status0 = status0;
765
        return;
bellard authored
766
    }
767
    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
pbrook authored
768
        qemu_set_irq(fdctrl->irq, 1);
769
        fdctrl->sra |= FD_SRA_INTPEND;
770
    }
771
    fdctrl->reset_sensei = 0;
772
773
    fdctrl->status0 = status0;
    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
774
775
}
bellard authored
776
/* Reset controller */
777
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
778
779
780
{
    int i;
bellard authored
781
    FLOPPY_DPRINTF("reset controller\n");
782
    fdctrl_reset_irq(fdctrl);
bellard authored
783
    /* Initialise controller */
784
785
786
787
    fdctrl->sra = 0;
    fdctrl->srb = 0xc0;
    if (!fdctrl->drives[1].bs)
        fdctrl->sra |= FD_SRA_nDRV2;
788
    fdctrl->cur_drv = 0;
789
    fdctrl->dor = FD_DOR_nRESET;
790
    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
791
    fdctrl->msr = FD_MSR_RQM;
792
    /* FIFO state */
793
794
    fdctrl->data_pos = 0;
    fdctrl->data_len = 0;
795
    fdctrl->data_state = 0;
796
    fdctrl->data_dir = FD_DIR_WRITE;
797
    for (i = 0; i < MAX_FD; i++)
798
        fd_recalibrate(&fdctrl->drives[i]);
799
    fdctrl_reset_fifo(fdctrl);
800
    if (do_irq) {
801
        fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
802
        fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
803
    }
804
805
806
807
}

static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
{
808
    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
809
810
811
812
}

static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
{
813
814
815
816
    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
        return &fdctrl->drives[1];
    else
        return &fdctrl->drives[0];
817
818
}
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
#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
837
838
static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
{
839
840
841
842
843
844
845
846
847
    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;
    }
848
849
}
850
851
852
853
854
855
856
857
858
859
/* 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;
}
860
/* Status B register : 0x01 (read-only) */
861
static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
862
{
863
864
865
866
867
    uint32_t retval = fdctrl->srb;

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

    return retval;
868
869
870
}

/* Digital output register : 0x02 */
871
static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
872
{
873
    uint32_t retval = fdctrl->dor;
874
875

    /* Selected drive */
876
    retval |= fdctrl->cur_drv;
877
878
879
880
881
    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);

    return retval;
}
882
static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
883
884
{
    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901

    /* 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;
902
    /* Reset */
903
    if (!(value & FD_DOR_nRESET)) {
904
        if (fdctrl->dor & FD_DOR_nRESET) {
bellard authored
905
            FLOPPY_DPRINTF("controller enter RESET state\n");
906
907
        }
    } else {
908
        if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
909
            FLOPPY_DPRINTF("controller out of RESET state\n");
910
            fdctrl_reset(fdctrl, 1);
911
            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
912
913
914
        }
    }
    /* Selected drive */
915
    fdctrl->cur_drv = value & FD_DOR_SELMASK;
916
917

    fdctrl->dor = value;
918
919
920
}

/* Tape drive register : 0x03 */
921
static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
922
{
923
    uint32_t retval = fdctrl->tdr;
924
925
926
927
928
929

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

    return retval;
}
930
static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
931
932
{
    /* Reset mode */
933
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
934
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
935
936
937
938
        return;
    }
    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
    /* Disk boot selection indicator */
939
    fdctrl->tdr = value & FD_TDR_BOOTSEL;
940
941
942
943
    /* Tape indicators: never allow */
}

/* Main status register : 0x04 (read) */
944
static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
945
{
946
    uint32_t retval = fdctrl->msr;
947
948
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
949
    fdctrl->dor |= FD_DOR_nRESET;
950
951
952
953
954
955
956
    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);

    return retval;
}

/* Data select rate register : 0x04 (write) */
957
static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
958
959
{
    /* Reset mode */
960
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
961
962
963
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
964
965
    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
    /* Reset: autoclear */
966
    if (value & FD_DSR_SWRESET) {
967
        fdctrl->dor &= ~FD_DOR_nRESET;
968
        fdctrl_reset(fdctrl, 1);
969
        fdctrl->dor |= FD_DOR_nRESET;
970
    }
971
    if (value & FD_DSR_PWRDOWN) {
972
        fdctrl_reset(fdctrl, 1);
973
    }
974
    fdctrl->dsr = value;
975
976
}
bellard authored
977
978
979
static int fdctrl_media_changed(fdrive_t *drv)
{
    int ret;
980
981
    if (!drv->bs)
bellard authored
982
983
984
985
986
987
988
989
        return 0;
    ret = bdrv_media_changed(drv->bs);
    if (ret) {
        fd_revalidate(drv);
    }
    return ret;
}
990
/* Digital input register : 0x07 (read-only) */
991
static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
992
993
994
{
    uint32_t retval = 0;
995
996
997
998
999
1000
1001
    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
        )
1002
        retval |= FD_DIR_DSKCHG;
1003
    if (retval != 0)
1004
        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
1005
1006
1007
1008
1009

    return retval;
}

/* FIFO state control */
1010
static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
1011
{
1012
1013
    fdctrl->data_dir = FD_DIR_WRITE;
    fdctrl->data_pos = 0;
1014
    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
1015
1016
1017
}

/* Set FIFO status for the host to read */
1018
static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
1019
{
1020
1021
1022
    fdctrl->data_dir = FD_DIR_READ;
    fdctrl->data_len = fifo_len;
    fdctrl->data_pos = 0;
1023
    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
1024
    if (do_irq)
1025
        fdctrl_raise_irq(fdctrl, 0x00);
1026
1027
1028
}

/* Set an error: unimplemented/unknown command */
1029
static void fdctrl_unimplemented (fdctrl_t *fdctrl, int direction)
1030
{
1031
    FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]);
1032
    fdctrl->fifo[0] = FD_SR0_INVCMD;
1033
    fdctrl_set_fifo(fdctrl, 1, 0);
1034
1035
}
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
/* 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;
}
1070
/* Callback for transfer end (stop or abort) */
1071
static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
1072
                                  uint8_t status1, uint8_t status2)
1073
{
1074
    fdrive_t *cur_drv;
1075
1076
    cur_drv = get_cur_drv(fdctrl);
1077
1078
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
                   status0, status1, status2,
1079
1080
                   status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl));
    fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1081
1082
1083
1084
1085
1086
1087
    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;
1088
    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1089
        DMA_release_DREQ(fdctrl->dma_chann);
1090
    }
1091
    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
1092
    fdctrl->msr &= ~FD_MSR_NONDMA;
1093
    fdctrl_set_fifo(fdctrl, 7, 1);
1094
1095
1096
}

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

    return;
}

/* Prepare a transfer of deleted data */
1199
static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
1200
{
1201
1202
    FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n");
1203
1204
1205
    /* We don't handle deleted data,
     * so we don't return *ANYTHING*
     */
1206
    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1207
1208
1209
}

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

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

    return retval;
}
1368
static void fdctrl_format_sector (fdctrl_t *fdctrl)
1369
{
1370
1371
    fdrive_t *cur_drv;
    uint8_t kh, kt, ks;
1372
1373
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1374
1375
1376
1377
1378
    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",
1379
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1380
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
1381
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1382
1383
    case 2:
        /* sect too big */
1384
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1385
1386
1387
1388
1389
1390
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
1391
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1392
1393
1394
1395
1396
1397
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
1398
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 1:
        fdctrl->data_state |= FD_STATE_SEEK;
        break;
    default:
        break;
    }
    memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
    if (cur_drv->bs == NULL ||
        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
ths authored
1412
        FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
1413
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1414
    } else {
1415
1416
1417
1418
        if (cur_drv->sect == cur_drv->last_sect) {
            fdctrl->data_state &= ~FD_STATE_FORMAT;
            /* Last sector done */
            if (FD_DID_SEEK(fdctrl->data_state))
1419
                fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1420
1421
1422
1423
1424
1425
1426
            else
                fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        } else {
            /* More to do */
            fdctrl->data_pos = 0;
            fdctrl->data_len = 4;
        }
1427
1428
1429
    }
}
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
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;
1444
1445
1446
1447
#if MAX_FD == 4
    fdctrl->fifo[2] = drv2(fdctrl)->track;
    fdctrl->fifo[3] = drv3(fdctrl)->track;
#else
1448
1449
    fdctrl->fifo[2] = 0;
    fdctrl->fifo[3] = 0;
1450
#endif
1451
1452
    /* timers */
    fdctrl->fifo[4] = fdctrl->timer0;
1453
    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
    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];
1482
1483
1484
1485
#if MAX_FD == 4
    drv2(fdctrl)->track = fdctrl->fifo[5];
    drv3(fdctrl)->track = fdctrl->fifo[6];
#endif
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
    /* 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;
1507
1508
1509
1510
#if MAX_FD == 4
    fdctrl->fifo[4] = drv2(fdctrl)->track;
    fdctrl->fifo[5] = drv3(fdctrl)->track;
#else
1511
1512
    fdctrl->fifo[4] = 0;
    fdctrl->fifo[5] = 0;
1513
#endif
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
    /* 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;
1542
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
    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;
1571
1572
1573
1574
    if (fdctrl->fifo[2] & 1)
        fdctrl->dor &= ~FD_DOR_DMAEN;
    else
        fdctrl->dor |= FD_DOR_DMAEN;
1575
1576
1577
1578
1579
1580
1581
1582
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

static void fdctrl_handle_sense_drive_status (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1583
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1584
1585
1586
1587
1588
1589
    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) |
1590
        GET_CUR_DRV(fdctrl) |
1591
1592
1593
1594
1595
1596
1597
1598
        0x28;
    fdctrl_set_fifo(fdctrl, 1, 0);
}

static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1599
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
    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);
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
    if(fdctrl->reset_sensei > 0) {
        fdctrl->fifo[0] =
            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
        fdctrl->reset_sensei--;
    } else {
        /* XXX: status0 handling is broken for read/write
           commands, so we do this hack. It should be suppressed
           ASAP */
        fdctrl->fifo[0] =
            FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
    }
1623
1624
1625
    fdctrl->fifo[1] = cur_drv->track;
    fdctrl_set_fifo(fdctrl, 2, 0);
    fdctrl_reset_irq(fdctrl);
1626
    fdctrl->status0 = FD_SR0_RDYCHG;
1627
1628
1629
1630
1631
1632
}

static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1633
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
    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 */
1652
    fdctrl_reset_fifo(fdctrl);
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
}

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 |
1693
            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1694
1695
1696
1697
1698
1699
        fdctrl_set_fifo(fdctrl, 1, 1);
    }
}

static void fdctrl_handle_relative_seek_out (fdctrl_t *fdctrl, int direction)
{
1700
    fdrive_t *cur_drv;
1701
1702
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1703
1704
1705
1706
1707
1708
1709
    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);
1710
    /* Raise Interrupt */
1711
1712
1713
1714
1715
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

static void fdctrl_handle_relative_seek_in (fdctrl_t *fdctrl, int direction)
{
1716
    fdrive_t *cur_drv;
1717
1718
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
    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);
}
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
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
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];
1774
1775
1776
static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
{
    fdrive_t *cur_drv;
1777
    int pos;
1778
1779
    /* Reset mode */
1780
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
bellard authored
1781
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
1782
1783
        return;
    }
1784
1785
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
        FLOPPY_ERROR("controller not ready for writing\n");
1786
1787
        return;
    }
1788
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
1789
    /* Is it write command time ? */
1790
    if (fdctrl->msr & FD_MSR_NONDMA) {
1791
        /* FIFO data write */
1792
1793
1794
1795
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
1796
            fdctrl->data_pos == fdctrl->data_len) {
1797
1798
1799
1800
1801
            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;
            }
1802
1803
1804
1805
1806
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                FLOPPY_DPRINTF("error seeking to next sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
1807
        }
1808
        /* Switch from transfer mode to status mode
1809
1810
         * then from status mode to command mode
         */
1811
        if (fdctrl->data_pos == fdctrl->data_len)
1812
            fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1813
1814
        return;
    }
1815
    if (fdctrl->data_pos == 0) {
1816
        /* Command */
1817
1818
1819
        pos = command_to_handler[value & 0xff];
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        fdctrl->data_len = handlers[pos].parameters + 1;
1820
    }
1821
1822
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
1823
1824
    fdctrl->fifo[fdctrl->data_pos++] = value;
    if (fdctrl->data_pos == fdctrl->data_len) {
1825
1826
1827
        /* We now have all parameters
         * and will be able to treat the command
         */
1828
1829
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            fdctrl_format_sector(fdctrl);
1830
1831
            return;
        }
1832
1833
1834
1835
        pos = command_to_handler[fdctrl->fifo[0] & 0xff];
        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
1836
1837
    }
}
1838
1839
1840
1841

static void fdctrl_result_timer(void *opaque)
{
    fdctrl_t *fdctrl = opaque;
1842
    fdrive_t *cur_drv = get_cur_drv(fdctrl);
1843
1844
1845
1846
1847
1848
1849
1850
    /* 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;
    }
1851
1852
    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
}
1853
1854

/* Init functions */
1855
static void fdctrl_connect_drives(fdctrl_t *fdctrl, BlockDriverState **fds)
1856
{
1857
    unsigned int i;
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868

    for (i = 0; i < MAX_FD; i++) {
        fd_init(&fdctrl->drives[i], fds[i]);
        fd_revalidate(&fdctrl->drives[i]);
    }
}

fdctrl_t *fdctrl_init (qemu_irq irq, int dma_chann, int mem_mapped,
                       target_phys_addr_t io_base,
                       BlockDriverState **fds)
{
Blue Swirl authored
1869
1870
    DeviceState *dev;
    SysBusDevice *s;
1871
1872
    fdctrl_t *fdctrl;
Blue Swirl authored
1873
1874
1875
1876
1877
    dev = qdev_create(NULL, "fdc");
    qdev_init(dev);
    s = sysbus_from_qdev(dev);
    sysbus_connect_irq(s, 0, irq);
    fdctrl = FROM_SYSBUS(fdctrl_t, s);
1878
    if (mem_mapped) {
Blue Swirl authored
1879
        sysbus_mmio_map(s, 0, io_base);
1880
    } else {
blueswir1 authored
1881
1882
1883
1884
1885
1886
1887
1888
        register_ioport_read((uint32_t)io_base + 0x01, 5, 1,
                             &fdctrl_read_port, fdctrl);
        register_ioport_read((uint32_t)io_base + 0x07, 1, 1,
                             &fdctrl_read_port, fdctrl);
        register_ioport_write((uint32_t)io_base + 0x01, 5, 1,
                              &fdctrl_write_port, fdctrl);
        register_ioport_write((uint32_t)io_base + 0x07, 1, 1,
                              &fdctrl_write_port, fdctrl);
1889
    }
1890
1891
    fdctrl->dma_chann = dma_chann;
    DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
1892
1893
    fdctrl_connect_drives(fdctrl, fds);
Blue Swirl authored
1894
1895
1896
1897
1898
1899
1900
    return fdctrl;
}

fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base,
                             BlockDriverState **fds, qemu_irq *fdc_tc)
{
Blue Swirl authored
1901
1902
    DeviceState *dev;
    SysBusDevice *s;
1903
1904
    fdctrl_t *fdctrl;
1905
    dev = qdev_create(NULL, "SUNW,fdtwo");
Blue Swirl authored
1906
1907
1908
1909
1910
1911
1912
    qdev_init(dev);
    s = sysbus_from_qdev(dev);
    sysbus_connect_irq(s, 0, irq);
    sysbus_mmio_map(s, 0, io_base);
    *fdc_tc = qdev_get_gpio_in(dev, 0);

    fdctrl = FROM_SYSBUS(fdctrl_t, s);
1913
1914
1915
1916

    fdctrl->dma_chann = -1;

    fdctrl_connect_drives(fdctrl, fds);
1917
1918
1919

    return fdctrl;
}
Blue Swirl authored
1920
1921
1922
static void fdctrl_init_common(SysBusDevice *dev, fdctrl_t *fdctrl,
                               int is_sun4m, int io)
Blue Swirl authored
1923
{
1924
1925
    int i, j;
    static int command_tables_inited = 0;
Blue Swirl authored
1926
1927
    sysbus_init_irq(dev, &fdctrl->irq);
Blue Swirl authored
1928
1929
    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
    sysbus_init_mmio(dev, 0x08, io);
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954

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

    FLOPPY_DPRINTF("init controller\n");
    fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
    fdctrl->result_timer = qemu_new_timer(vm_clock,
                                          fdctrl_result_timer, fdctrl);

    fdctrl->version = 0x90; /* Intel 82078 controller */
    fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
    fdctrl->sun4m = is_sun4m;

    fdctrl_external_reset(fdctrl);
    register_savevm("fdc", -1, 2, fdc_save, fdc_load, fdctrl);
    qemu_register_reset(fdctrl_external_reset, fdctrl);
Blue Swirl authored
1955
1956
}
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
static void fdc_init1(SysBusDevice *dev)
{
    fdctrl_t *fdctrl = FROM_SYSBUS(fdctrl_t, dev);
    int io;

    io = cpu_register_io_memory(fdctrl_mem_read, fdctrl_mem_write, fdctrl);
    fdctrl_init_common(dev, fdctrl, 0, io);
}

static void sun4m_fdc_init1(SysBusDevice *dev)
{
    fdctrl_t *fdctrl = FROM_SYSBUS(fdctrl_t, dev);
    int io;

    io = cpu_register_io_memory(fdctrl_mem_read_strict,
                                fdctrl_mem_write_strict, fdctrl);
    fdctrl_init_common(dev, fdctrl, 1, io);
}
Blue Swirl authored
1975
1976
1977
1978
1979

static SysBusDeviceInfo fdc_info = {
    .init = fdc_init1,
    .qdev.name  = "fdc",
    .qdev.size  = sizeof(fdctrl_t),
1980
1981
1982
1983
1984
1985
};

static SysBusDeviceInfo sun4m_fdc_info = {
    .init = sun4m_fdc_init1,
    .qdev.name  = "SUNW,fdtwo",
    .qdev.size  = sizeof(fdctrl_t),
Blue Swirl authored
1986
1987
1988
1989
1990
};

static void fdc_register_devices(void)
{
    sysbus_register_withprop(&fdc_info);
1991
    sysbus_register_withprop(&sun4m_fdc_info);
Blue Swirl authored
1992
1993
1994
}

device_init(fdc_register_devices)