Blame view

hw/fdc.c 59.6 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

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

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

/********************************************************/
/* 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
/* Will always be a fixed parameter for us */
56
57
58
#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 */
59
60
61
62
63
64

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

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

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

/* 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;
}
177
178
179
180
181
182
183
/* 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;
184
    const char *str;
185
186
} fd_format_t;
blueswir1 authored
187
static const fd_format_t fd_formats[] = {
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
    /* 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", },
225
    /* 320 kB 5"1/4 floppy disks */
226
227
228
229
230
231
232
233
    { 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, },
};
234
/* Revalidate a disk drive after a disk change */
235
static void fd_revalidate (fdrive_t *drv)
236
{
blueswir1 authored
237
    const fd_format_t *parse;
238
    uint64_t nb_sectors, size;
239
    int i, first_match, match;
240
    int nb_heads, max_track, last_sect, ro;
241
242

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

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

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

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

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

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

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

    return retval;
}

static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    fdctrl_t *fdctrl = opaque;
553
554
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
blueswir1 authored
555
    switch (reg) {
556
    case FD_REG_DOR:
557
558
        fdctrl_write_dor(fdctrl, value);
        break;
559
    case FD_REG_TDR:
560
        fdctrl_write_tape(fdctrl, value);
561
        break;
562
    case FD_REG_DSR:
563
        fdctrl_write_rate(fdctrl, value);
564
        break;
565
    case FD_REG_FIFO:
566
        fdctrl_write_data(fdctrl, value);
567
        break;
568
    default:
569
        break;
570
    }
571
572
}
blueswir1 authored
573
574
575
576
577
578
579
580
581
582
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);
}
583
584
static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
{
585
    return fdctrl_read(opaque, (uint32_t)reg);
586
587
}
588
static void fdctrl_write_mem (void *opaque,
589
590
                              target_phys_addr_t reg, uint32_t value)
{
591
    fdctrl_write(opaque, (uint32_t)reg, value);
592
593
}
bellard authored
594
static CPUReadMemoryFunc *fdctrl_mem_read[3] = {
595
596
597
    fdctrl_read_mem,
    fdctrl_read_mem,
    fdctrl_read_mem,
bellard authored
598
599
600
};

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

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

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

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

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

    return ret;
}

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

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

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

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

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

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

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

    return retval;
865
866
867
}

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

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

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

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

    fdctrl->dor = value;
915
916
917
}

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

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

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

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

    return retval;
}

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

    return retval;
}

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

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

/* Set an error: unimplemented/unknown command */
1026
static void fdctrl_unimplemented (fdctrl_t *fdctrl, int direction)
1027
{
1028
    FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]);
1029
    fdctrl->fifo[0] = FD_SR0_INVCMD;
1030
    fdctrl_set_fifo(fdctrl, 1, 0);
1031
1032
}
1033
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
/* 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;
}
1067
/* Callback for transfer end (stop or abort) */
1068
static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
1069
                                  uint8_t status1, uint8_t status2)
1070
{
1071
    fdrive_t *cur_drv;
1072
1073
    cur_drv = get_cur_drv(fdctrl);
1074
1075
    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
                   status0, status1, status2,
1076
1077
                   status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl));
    fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1078
1079
1080
1081
1082
1083
1084
    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;
1085
    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1086
        DMA_release_DREQ(fdctrl->dma_chann);
1087
    }
1088
    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
1089
    fdctrl->msr &= ~FD_MSR_NONDMA;
1090
    fdctrl_set_fifo(fdctrl, 7, 1);
1091
1092
1093
}

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

    return;
}

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

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

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

    return retval;
}
1365
static void fdctrl_format_sector (fdctrl_t *fdctrl)
1366
{
1367
1368
    fdrive_t *cur_drv;
    uint8_t kh, kt, ks;
1369
1370
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1371
1372
1373
1374
1375
    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",
1376
                   GET_CUR_DRV(fdctrl), kh, kt, ks,
1377
                   _fd_sector(kh, kt, ks, cur_drv->last_sect));
1378
    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1379
1380
    case 2:
        /* sect too big */
1381
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1382
1383
1384
1385
1386
1387
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 3:
        /* track too big */
1388
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1389
1390
1391
1392
1393
1394
        fdctrl->fifo[3] = kt;
        fdctrl->fifo[4] = kh;
        fdctrl->fifo[5] = ks;
        return;
    case 4:
        /* No seek enabled */
1395
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
        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
1409
        FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
1410
        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1411
    } else {
1412
1413
1414
1415
        if (cur_drv->sect == cur_drv->last_sect) {
            fdctrl->data_state &= ~FD_STATE_FORMAT;
            /* Last sector done */
            if (FD_DID_SEEK(fdctrl->data_state))
1416
                fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1417
1418
1419
1420
1421
1422
1423
            else
                fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        } else {
            /* More to do */
            fdctrl->data_pos = 0;
            fdctrl->data_len = 4;
        }
1424
1425
1426
    }
}
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
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;
1441
1442
1443
1444
#if MAX_FD == 4
    fdctrl->fifo[2] = drv2(fdctrl)->track;
    fdctrl->fifo[3] = drv3(fdctrl)->track;
#else
1445
1446
    fdctrl->fifo[2] = 0;
    fdctrl->fifo[3] = 0;
1447
#endif
1448
1449
    /* timers */
    fdctrl->fifo[4] = fdctrl->timer0;
1450
    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1451
1452
1453
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
    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];
1479
1480
1481
1482
#if MAX_FD == 4
    drv2(fdctrl)->track = fdctrl->fifo[5];
    drv3(fdctrl)->track = fdctrl->fifo[6];
#endif
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
    /* 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;
1504
1505
1506
1507
#if MAX_FD == 4
    fdctrl->fifo[4] = drv2(fdctrl)->track;
    fdctrl->fifo[5] = drv3(fdctrl)->track;
#else
1508
1509
    fdctrl->fifo[4] = 0;
    fdctrl->fifo[5] = 0;
1510
#endif
1511
1512
1513
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
    /* 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;
1539
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1540
1541
1542
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
    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;
1568
1569
1570
1571
    if (fdctrl->fifo[2] & 1)
        fdctrl->dor &= ~FD_DOR_DMAEN;
    else
        fdctrl->dor |= FD_DOR_DMAEN;
1572
1573
1574
1575
1576
1577
1578
1579
    /* No result back */
    fdctrl_reset_fifo(fdctrl);
}

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

static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1596
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
    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);
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
    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);
    }
1620
1621
1622
    fdctrl->fifo[1] = cur_drv->track;
    fdctrl_set_fifo(fdctrl, 2, 0);
    fdctrl_reset_irq(fdctrl);
1623
    fdctrl->status0 = FD_SR0_RDYCHG;
1624
1625
1626
1627
1628
1629
}

static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction)
{
    fdrive_t *cur_drv;
1630
    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
    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 */
1649
    fdctrl_reset_fifo(fdctrl);
1650
1651
1652
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
}

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

static void fdctrl_handle_relative_seek_out (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
    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);
1707
    /* Raise Interrupt */
1708
1709
1710
1711
1712
    fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
}

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

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

/* 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 */
1860
    for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) {
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
        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));
    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->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]);
    }
1884
1885
    fdctrl_external_reset(fdctrl);
    register_savevm("fdc", io_base, 2, fdc_save, fdc_load, fdctrl);
1886
    qemu_register_reset(fdctrl_external_reset, 0, fdctrl);
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
    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 {
blueswir1 authored
1909
1910
1911
1912
1913
1914
1915
1916
        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);
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
    }

    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;
1928
    fdctrl = fdctrl_init_common(irq, -1, io_base, fds);
1929
1930
1931
1932
1933
1934
1935
1936
1937
    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;
}