VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/fdc.c@ 37459

Last change on this file since 37459 was 37459, checked in by vboxsync, 13 years ago

Set busy bit when starting a command.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 87.8 KB
Line 
1/* $Id: fdc.c 37459 2011-06-14 21:54:45Z vboxsync $ */
2/** @file
3 * VBox storage devices: Floppy disk controller
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * QEMU Floppy disk emulator (Intel 82078)
21 *
22 * Copyright (c) 2003 Jocelyn Mayer
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 *
42 */
43
44
45/*******************************************************************************
46* Header Files *
47*******************************************************************************/
48#define LOG_GROUP LOG_GROUP_DEV_FDC
49#include <VBox/vmm/pdmdev.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52#include <iprt/uuid.h>
53
54#include "VBoxDD.h"
55#include "vl_vbox.h"
56
57#define FDC_SAVESTATE_CURRENT 2 /* The new and improved saved state. */
58#define FDC_SAVESTATE_OLD 1 /* The original saved state. */
59
60#define MAX_FD 2
61
62#define PDMIBASE_2_FDRIVE(pInterface) \
63 ((fdrive_t *)((uintptr_t)(pInterface) - RT_OFFSETOF(fdrive_t, IBase)))
64
65#define PDMIMOUNTNOTIFY_2_FDRIVE(p) \
66 ((fdrive_t *)((uintptr_t)(p) - RT_OFFSETOF(fdrive_t, IMountNotify)))
67
68
69/********************************************************/
70/* debug Floppy devices */
71/* #define DEBUG_FLOPPY */
72
73#ifndef VBOX
74 #ifdef DEBUG_FLOPPY
75 #define FLOPPY_DPRINTF(fmt, args...) \
76 do { printf("FLOPPY: " fmt , ##args); } while (0)
77 #endif
78#else /* !VBOX */
79 # ifdef LOG_ENABLED
80 static void FLOPPY_DPRINTF (const char *fmt, ...)
81 {
82 if (LogIsEnabled ()) {
83 va_list args;
84 va_start (args, fmt);
85 RTLogLogger (NULL, NULL, "floppy: %N", fmt, &args); /* %N - nested va_list * type formatting call. */
86 va_end (args);
87 }
88 }
89 # else
90 DECLINLINE(void) FLOPPY_DPRINTF(const char *pszFmt, ...) {}
91 # endif
92#endif /* !VBOX */
93
94#ifndef VBOX
95#define FLOPPY_ERROR(fmt, args...) \
96 do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)
97#else /* VBOX */
98# define FLOPPY_ERROR RTLogPrintf
99#endif /* VBOX */
100
101#ifdef VBOX
102typedef struct fdctrl_t fdctrl_t;
103#endif /* VBOX */
104
105/********************************************************/
106/* Floppy drive emulation */
107
108#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
109#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
110
111/* Will always be a fixed parameter for us */
112#define FD_SECTOR_LEN 512
113#define FD_SECTOR_SC 2 /* Sector size code */
114#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */
115
116/* Floppy disk drive emulation */
117typedef enum fdisk_type_t {
118 FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */
119 FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */
120 FDRIVE_DISK_720 = 0x03, /* 720 kB disk */
121 FDRIVE_DISK_USER = 0x04, /* User defined geometry */
122 FDRIVE_DISK_NONE = 0x05 /* No disk */
123} fdisk_type_t;
124
125typedef enum fdrive_type_t {
126 FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
127 FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
128 FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
129 FDRIVE_DRV_NONE = 0x03 /* No drive connected */
130} fdrive_type_t;
131
132typedef enum fdrive_flags_t {
133 FDISK_DBL_SIDES = 0x01
134} fdrive_flags_t;
135
136typedef enum fdrive_rate_t {
137 FDRIVE_RATE_500K = 0x00, /* 500 Kbps */
138 FDRIVE_RATE_300K = 0x01, /* 300 Kbps */
139 FDRIVE_RATE_250K = 0x02, /* 250 Kbps */
140 FDRIVE_RATE_1M = 0x03 /* 1 Mbps */
141} fdrive_rate_t;
142
143/**
144 * The status for one drive.
145 *
146 * @implements PDMIBASE
147 * @implements PDMIBLOCKPORT
148 * @implements PDMIMOUNTNOTIFY
149 */
150typedef struct fdrive_t {
151#ifndef VBOX
152 BlockDriverState *bs;
153#else /* VBOX */
154 /** Pointer to the attached driver's base interface. */
155 R3PTRTYPE(PPDMIBASE) pDrvBase;
156 /** Pointer to the attached driver's block interface. */
157 R3PTRTYPE(PPDMIBLOCK) pDrvBlock;
158 /** Pointer to the attached driver's block bios interface. */
159 R3PTRTYPE(PPDMIBLOCKBIOS) pDrvBlockBios;
160 /** Pointer to the attached driver's mount interface.
161 * This is NULL if the driver isn't a removable unit. */
162 R3PTRTYPE(PPDMIMOUNT) pDrvMount;
163 /** The base interface. */
164 PDMIBASE IBase;
165 /** The block port interface. */
166 PDMIBLOCKPORT IPort;
167 /** The mount notify interface. */
168 PDMIMOUNTNOTIFY IMountNotify;
169 /** The LUN #. */
170 RTUINT iLUN;
171 /** The LED for this LUN. */
172 PDMLED Led;
173#endif
174 /* Drive status */
175 fdrive_type_t drive;
176 uint8_t perpendicular; /* 2.88 MB access mode */
177 uint8_t dsk_chg; /* Disk change line */
178 /* Position */
179 uint8_t head;
180 uint8_t track;
181 uint8_t sect;
182 /* Media */
183 fdrive_flags_t flags;
184 uint8_t last_sect; /* Nb sector per track */
185 uint8_t max_track; /* Nb of tracks */
186 uint16_t bps; /* Bytes per sector */
187 uint8_t ro; /* Is read-only */
188 uint8_t media_rate; /* Data rate of medium */
189} fdrive_t;
190
191static void fd_init(fdrive_t *drv)
192{
193 /* Drive */
194 drv->drive = FDRIVE_DRV_NONE;
195 drv->perpendicular = 0;
196 /* Disk */
197 drv->last_sect = 0;
198 drv->max_track = 0;
199}
200
201static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
202 uint8_t last_sect)
203{
204 return (((track * 2) + head) * last_sect) + sect - 1; /* sect >= 1 */
205}
206
207/* Returns current position, in sectors, for given drive */
208static int fd_sector(fdrive_t *drv)
209{
210 return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect);
211}
212
213/* Seek to a new position:
214 * returns 0 if already on right track
215 * returns 1 if track changed
216 * returns 2 if track is invalid
217 * returns 3 if sector is invalid
218 * returns 4 if seek is disabled
219 */
220static int fd_seek(fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
221 int enable_seek)
222{
223 int sector;
224 int ret;
225
226 if (track > drv->max_track ||
227 (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
228 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
229 head, track, sect, 1,
230 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
231 drv->max_track, drv->last_sect);
232 return 2;
233 }
234 if (sect > drv->last_sect) {
235 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
236 head, track, sect, 1,
237 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
238 drv->max_track, drv->last_sect);
239 return 3;
240 }
241 sector = fd_sector_calc(head, track, sect, drv->last_sect);
242 ret = 0;
243 if (sector != fd_sector(drv)) {
244#if 0
245 if (!enable_seek) {
246 FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
247 head, track, sect, 1, drv->max_track, drv->last_sect);
248 return 4;
249 }
250#endif
251 drv->head = head;
252 if (drv->track != track)
253 ret = 1;
254 drv->track = track;
255 drv->sect = sect;
256 }
257
258 return ret;
259}
260
261/* Set drive back to track 0 */
262static void fd_recalibrate(fdrive_t *drv)
263{
264 FLOPPY_DPRINTF("recalibrate\n");
265 drv->head = 0;
266 drv->track = 0;
267 drv->sect = 1;
268}
269
270/* Recognize floppy formats */
271typedef struct fd_format_t {
272 fdrive_type_t drive;
273 fdisk_type_t disk;
274 uint8_t last_sect;
275 uint8_t max_track;
276 uint8_t max_head;
277 fdrive_rate_t rate;
278 const char *str;
279} fd_format_t;
280
281static fd_format_t fd_formats[] = {
282 /* First entry is default format */
283 /* 1.44 MB 3"1/2 floppy disks */
284 { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 3\"1/2", },
285 { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 3\"1/2", },
286 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, FDRIVE_RATE_500K, "1.68 MB 3\"1/2", },
287 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, FDRIVE_RATE_500K, "1.72 MB 3\"1/2", },
288 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, FDRIVE_RATE_500K, "1.74 MB 3\"1/2", },
289 { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, FDRIVE_RATE_500K, "1.76 MB 3\"1/2", },
290 { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, FDRIVE_RATE_500K, "1.84 MB 3\"1/2", },
291 { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, FDRIVE_RATE_500K, "1.92 MB 3\"1/2", },
292 /* 2.88 MB 3"1/2 floppy disks */
293 { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, FDRIVE_RATE_1M, "2.88 MB 3\"1/2", },
294 { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, FDRIVE_RATE_1M, "3.12 MB 3\"1/2", },
295 { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, FDRIVE_RATE_1M, "3.2 MB 3\"1/2", },
296 { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, FDRIVE_RATE_1M, "3.52 MB 3\"1/2", },
297 { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, FDRIVE_RATE_1M, "3.84 MB 3\"1/2", },
298 /* 720 kB 3"1/2 floppy disks */
299 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 3\"1/2", },
300 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, FDRIVE_RATE_250K, "800 kB 3\"1/2", },
301 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, FDRIVE_RATE_250K, "820 kB 3\"1/2", },
302 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, FDRIVE_RATE_250K, "830 kB 3\"1/2", },
303 { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, FDRIVE_RATE_250K, "1.04 MB 3\"1/2", },
304 { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, FDRIVE_RATE_250K, "1.12 MB 3\"1/2", },
305 /* 1.2 MB 5"1/4 floppy disks */
306 { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, FDRIVE_RATE_500K, "1.2 MB 5\"1/4", },
307 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, FDRIVE_RATE_500K, "1.44 MB 5\"1/4", },
308 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, FDRIVE_RATE_500K, "1.48 MB 5\"1/4", },
309 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, FDRIVE_RATE_500K, "1.49 MB 5\"1/4", },
310 { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, FDRIVE_RATE_500K, "1.6 MB 5\"1/4", },
311 /* 720 kB 5"1/4 floppy disks */
312 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, FDRIVE_RATE_250K, "720 kB 5\"1/4", },
313 { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, FDRIVE_RATE_250K, "880 kB 5\"1/4", },
314 /* 360 kB 5"1/4 floppy disks */
315 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, FDRIVE_RATE_300K, "360 kB 5\"1/4", },
316 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, FDRIVE_RATE_300K, "180 kB 5\"1/4", },
317 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, FDRIVE_RATE_300K, "410 kB 5\"1/4", },
318 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, FDRIVE_RATE_300K, "420 kB 5\"1/4", },
319 /* 320 kB 5"1/4 floppy disks */
320 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, FDRIVE_RATE_250K, "320 kB 5\"1/4", },
321 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, FDRIVE_RATE_250K, "160 kB 5\"1/4", },
322 /* 360 kB must match 5"1/4 better than 3"1/2... */
323 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, FDRIVE_RATE_250K, "360 kB 3\"1/2", },
324 /* end */
325 { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, 0, NULL, },
326};
327
328/* Revalidate a disk drive after a disk change */
329static void fd_revalidate(fdrive_t *drv)
330{
331 const fd_format_t *parse;
332 uint64_t nb_sectors, size;
333 int i, first_match, match;
334 int nb_heads, max_track, last_sect, ro;
335
336 FLOPPY_DPRINTF("revalidate\n");
337#ifndef VBOX
338 if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
339 ro = bdrv_is_read_only(drv->bs);
340 bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
341#else /* VBOX */
342 if (drv->pDrvBlock
343 && drv->pDrvMount
344 && drv->pDrvMount->pfnIsMounted (drv->pDrvMount)) {
345 ro = drv->pDrvBlock->pfnIsReadOnly (drv->pDrvBlock);
346 nb_heads = max_track = last_sect = 0;
347#endif /* VBOX */
348 if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
349 FLOPPY_DPRINTF("User defined disk (%d %d %d)",
350 nb_heads - 1, max_track, last_sect);
351 } else {
352#ifndef VBOX
353 bdrv_get_geometry(drv->bs, &nb_sectors);
354#else /* VBOX */
355 {
356 uint64_t size2 = drv->pDrvBlock->pfnGetSize (drv->pDrvBlock);
357 nb_sectors = size2 / FD_SECTOR_LEN;
358 }
359#endif /* VBOX */
360 match = -1;
361 first_match = -1;
362 for (i = 0;; i++) {
363 parse = &fd_formats[i];
364 if (parse->drive == FDRIVE_DRV_NONE)
365 break;
366 if (drv->drive == parse->drive ||
367 drv->drive == FDRIVE_DRV_NONE) {
368 size = (parse->max_head + 1) * parse->max_track *
369 parse->last_sect;
370 if (nb_sectors == size) {
371 match = i;
372 break;
373 }
374 if (first_match == -1)
375 first_match = i;
376 }
377 }
378 if (match == -1) {
379 if (first_match == -1)
380 match = 1;
381 else
382 match = first_match;
383 parse = &fd_formats[match];
384 }
385 nb_heads = parse->max_head + 1;
386 max_track = parse->max_track;
387 last_sect = parse->last_sect;
388 drv->drive = parse->drive;
389#ifdef VBOX
390 drv->media_rate = parse->rate;
391#endif
392 FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
393 nb_heads, max_track, last_sect, ro ? "ro" : "rw");
394 LogRel(("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
395 nb_heads, max_track, last_sect, ro ? "ro" : "rw"));
396 }
397 if (nb_heads == 1) {
398 drv->flags &= ~FDISK_DBL_SIDES;
399 } else {
400 drv->flags |= FDISK_DBL_SIDES;
401 }
402 drv->max_track = max_track;
403 drv->last_sect = last_sect;
404 drv->ro = ro;
405 } else {
406 FLOPPY_DPRINTF("No disk in drive\n");
407 drv->last_sect = 0;
408 drv->max_track = 0;
409 drv->flags &= ~FDISK_DBL_SIDES;
410 drv->dsk_chg = true; /* Disk change line active. */
411 }
412}
413
414/********************************************************/
415/* Intel 82078 floppy disk controller emulation */
416
417static void fdctrl_reset(fdctrl_t *fdctrl, int do_irq);
418static void fdctrl_reset_fifo(fdctrl_t *fdctrl);
419#ifndef VBOX
420static int fdctrl_transfer_handler (void *opaque, int nchan,
421 int dma_pos, int dma_len);
422#else /* VBOX: */
423static DECLCALLBACK(uint32_t) fdctrl_transfer_handler (PPDMDEVINS pDevIns,
424 void *opaque,
425 unsigned nchan,
426 uint32_t dma_pos,
427 uint32_t dma_len);
428#endif /* VBOX */
429static void fdctrl_raise_irq(fdctrl_t *fdctrl, uint8_t status0);
430static fdrive_t *get_cur_drv(fdctrl_t *fdctrl);
431
432static void fdctrl_result_timer(void *opaque);
433static uint32_t fdctrl_read_statusA(fdctrl_t *fdctrl);
434static uint32_t fdctrl_read_statusB(fdctrl_t *fdctrl);
435static uint32_t fdctrl_read_dor(fdctrl_t *fdctrl);
436static void fdctrl_write_dor(fdctrl_t *fdctrl, uint32_t value);
437static uint32_t fdctrl_read_tape(fdctrl_t *fdctrl);
438static void fdctrl_write_tape(fdctrl_t *fdctrl, uint32_t value);
439static uint32_t fdctrl_read_main_status(fdctrl_t *fdctrl);
440static void fdctrl_write_rate(fdctrl_t *fdctrl, uint32_t value);
441static uint32_t fdctrl_read_data(fdctrl_t *fdctrl);
442static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value);
443static uint32_t fdctrl_read_dir(fdctrl_t *fdctrl);
444static void fdctrl_write_ccr(fdctrl_t *fdctrl, uint32_t value);
445
446enum {
447 FD_DIR_WRITE = 0,
448 FD_DIR_READ = 1,
449 FD_DIR_SCANE = 2,
450 FD_DIR_SCANL = 3,
451 FD_DIR_SCANH = 4
452};
453
454enum {
455 FD_STATE_MULTI = 0x01, /* multi track flag */
456 FD_STATE_FORMAT = 0x02, /* format flag */
457 FD_STATE_SEEK = 0x04 /* seek flag */
458};
459
460enum {
461 FD_REG_SRA = 0x00,
462 FD_REG_SRB = 0x01,
463 FD_REG_DOR = 0x02,
464 FD_REG_TDR = 0x03,
465 FD_REG_MSR = 0x04,
466 FD_REG_DSR = 0x04,
467 FD_REG_FIFO = 0x05,
468 FD_REG_DIR = 0x07,
469 FD_REG_CCR = 0x07
470};
471
472enum {
473 FD_CMD_READ_TRACK = 0x02,
474 FD_CMD_SPECIFY = 0x03,
475 FD_CMD_SENSE_DRIVE_STATUS = 0x04,
476 FD_CMD_WRITE = 0x05,
477 FD_CMD_READ = 0x06,
478 FD_CMD_RECALIBRATE = 0x07,
479 FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
480 FD_CMD_WRITE_DELETED = 0x09,
481 FD_CMD_READ_ID = 0x0a,
482 FD_CMD_READ_DELETED = 0x0c,
483 FD_CMD_FORMAT_TRACK = 0x0d,
484 FD_CMD_DUMPREG = 0x0e,
485 FD_CMD_SEEK = 0x0f,
486 FD_CMD_VERSION = 0x10,
487 FD_CMD_SCAN_EQUAL = 0x11,
488 FD_CMD_PERPENDICULAR_MODE = 0x12,
489 FD_CMD_CONFIGURE = 0x13,
490 FD_CMD_LOCK = 0x14,
491 FD_CMD_VERIFY = 0x16,
492 FD_CMD_POWERDOWN_MODE = 0x17,
493 FD_CMD_PART_ID = 0x18,
494 FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
495 FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
496 FD_CMD_SAVE = 0x2e,
497 FD_CMD_OPTION = 0x33,
498 FD_CMD_RESTORE = 0x4e,
499 FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
500 FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
501 FD_CMD_FORMAT_AND_WRITE = 0xcd,
502 FD_CMD_RELATIVE_SEEK_IN = 0xcf
503};
504
505enum {
506 FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */
507 FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */
508 FD_CONFIG_POLL = 0x10, /* Poll enabled */
509 FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */
510 FD_CONFIG_EIS = 0x40 /* No implied seeks */
511};
512
513enum {
514 FD_SR0_EQPMT = 0x10,
515 FD_SR0_SEEK = 0x20,
516 FD_SR0_ABNTERM = 0x40,
517 FD_SR0_INVCMD = 0x80,
518 FD_SR0_RDYCHG = 0xc0
519};
520
521enum {
522 FD_SR1_MA = 0x01, /* Missing address mark */
523 FD_SR1_NW = 0x02, /* Not writable */
524 FD_SR1_EC = 0x80 /* End of cylinder */
525};
526
527enum {
528 FD_SR2_SNS = 0x04, /* Scan not satisfied */
529 FD_SR2_SEH = 0x08 /* Scan equal hit */
530};
531
532enum {
533 FD_SRA_DIR = 0x01,
534 FD_SRA_nWP = 0x02,
535 FD_SRA_nINDX = 0x04,
536 FD_SRA_HDSEL = 0x08,
537 FD_SRA_nTRK0 = 0x10,
538 FD_SRA_STEP = 0x20,
539 FD_SRA_nDRV2 = 0x40,
540 FD_SRA_INTPEND = 0x80
541};
542
543enum {
544 FD_SRB_MTR0 = 0x01,
545 FD_SRB_MTR1 = 0x02,
546 FD_SRB_WGATE = 0x04,
547 FD_SRB_RDATA = 0x08,
548 FD_SRB_WDATA = 0x10,
549 FD_SRB_DR0 = 0x20
550};
551
552enum {
553#if MAX_FD == 4
554 FD_DOR_SELMASK = 0x03,
555#else
556 FD_DOR_SELMASK = 0x01,
557#endif
558 FD_DOR_nRESET = 0x04,
559 FD_DOR_DMAEN = 0x08,
560 FD_DOR_MOTEN0 = 0x10,
561 FD_DOR_MOTEN1 = 0x20,
562 FD_DOR_MOTEN2 = 0x40,
563 FD_DOR_MOTEN3 = 0x80
564};
565
566enum {
567#if MAX_FD == 4
568 FD_TDR_BOOTSEL = 0x0c
569#else
570 FD_TDR_BOOTSEL = 0x04
571#endif
572};
573
574enum {
575 FD_DSR_DRATEMASK= 0x03,
576 FD_DSR_PWRDOWN = 0x40,
577 FD_DSR_SWRESET = 0x80
578};
579
580enum {
581 FD_MSR_DRV0BUSY = 0x01,
582 FD_MSR_DRV1BUSY = 0x02,
583 FD_MSR_DRV2BUSY = 0x04,
584 FD_MSR_DRV3BUSY = 0x08,
585 FD_MSR_CMDBUSY = 0x10,
586 FD_MSR_NONDMA = 0x20,
587 FD_MSR_DIO = 0x40,
588 FD_MSR_RQM = 0x80
589};
590
591enum {
592 FD_DIR_DSKCHG = 0x80
593};
594
595#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
596#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
597#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
598
599#ifdef VBOX
600/**
601 * Floppy controller state.
602 *
603 * @implements PDMILEDPORTS
604 */
605#endif
606struct fdctrl_t {
607#ifndef VBOX
608 fdctrl_t *fdctrl;
609#endif
610 /* Controller's identification */
611 uint8_t version;
612 /* HW */
613#ifndef VBOX
614 int irq;
615 int dma_chann;
616#else
617 uint8_t irq_lvl;
618 uint8_t dma_chann;
619#endif
620 uint32_t io_base;
621 /* Controller state */
622 QEMUTimer *result_timer;
623 uint8_t sra;
624 uint8_t srb;
625 uint8_t dor;
626 uint8_t tdr;
627 uint8_t dsr;
628 uint8_t msr;
629 uint8_t cur_drv;
630 uint8_t status0;
631 uint8_t status1;
632 uint8_t status2;
633 /* Command FIFO */
634 uint8_t fifo[FD_SECTOR_LEN];
635 uint32_t data_pos;
636 uint32_t data_len;
637 uint8_t data_state;
638 uint8_t data_dir;
639 uint8_t eot; /* last wanted sector */
640 /* States kept only to be returned back */
641 /* Timers state */
642 uint8_t timer0;
643 uint8_t timer1;
644 /* precompensation */
645 uint8_t precomp_trk;
646 uint8_t config;
647 uint8_t lock;
648 /* Power down config (also with status regB access mode */
649 uint8_t pwrd;
650 /* Floppy drives */
651 uint8_t num_floppies;
652 fdrive_t drives[MAX_FD];
653 uint8_t reset_sensei;
654#ifdef VBOX
655 /** Pointer to device instance. */
656 PPDMDEVINS pDevIns;
657
658 /** Status LUN: The base interface. */
659 PDMIBASE IBaseStatus;
660 /** Status LUN: The Leds interface. */
661 PDMILEDPORTS ILeds;
662 /** Status LUN: The Partner of ILeds. */
663 PPDMILEDCONNECTORS pLedsConnector;
664#endif
665};
666
667static uint32_t fdctrl_read (void *opaque, uint32_t reg)
668{
669 fdctrl_t *fdctrl = opaque;
670 uint32_t retval;
671
672 switch (reg) {
673 case FD_REG_SRA:
674 retval = fdctrl_read_statusA(fdctrl);
675 break;
676 case FD_REG_SRB:
677 retval = fdctrl_read_statusB(fdctrl);
678 break;
679 case FD_REG_DOR:
680 retval = fdctrl_read_dor(fdctrl);
681 break;
682 case FD_REG_TDR:
683 retval = fdctrl_read_tape(fdctrl);
684 break;
685 case FD_REG_MSR:
686 retval = fdctrl_read_main_status(fdctrl);
687 break;
688 case FD_REG_FIFO:
689 retval = fdctrl_read_data(fdctrl);
690 break;
691 case FD_REG_DIR:
692 retval = fdctrl_read_dir(fdctrl);
693 break;
694 default:
695 retval = (uint32_t)(-1);
696 break;
697 }
698 FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
699
700 return retval;
701}
702
703static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
704{
705 fdctrl_t *fdctrl = opaque;
706
707 FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
708
709 switch (reg) {
710 case FD_REG_DOR:
711 fdctrl_write_dor(fdctrl, value);
712 break;
713 case FD_REG_TDR:
714 fdctrl_write_tape(fdctrl, value);
715 break;
716 case FD_REG_DSR:
717 fdctrl_write_rate(fdctrl, value);
718 break;
719 case FD_REG_FIFO:
720 fdctrl_write_data(fdctrl, value);
721 break;
722 case FD_REG_CCR:
723 fdctrl_write_ccr(fdctrl, value);
724 break;
725 default:
726 break;
727 }
728}
729
730#ifdef VBOX
731/**
732 * Called when a medium is mounted.
733 *
734 * @param pInterface Pointer to the interface structure
735 * containing the called function pointer.
736 */
737static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface)
738{
739 fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
740 LogFlow(("fdMountNotify:\n"));
741 fd_revalidate(drv);
742}
743
744/**
745 * Called when a medium is unmounted.
746 * @param pInterface Pointer to the interface structure
747 * containing the called function pointer.
748 */
749static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
750{
751 fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
752 LogFlow(("fdUnmountNotify:\n"));
753 fd_revalidate(drv);
754}
755#endif
756
757/* Change IRQ state */
758static void fdctrl_reset_irq(fdctrl_t *fdctrl)
759{
760 if (!(fdctrl->sra & FD_SRA_INTPEND))
761 return;
762 FLOPPY_DPRINTF("Reset interrupt\n");
763#ifdef VBOX
764 PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 0);
765#else
766 qemu_set_irq(fdctrl->irq, 0);
767#endif
768 fdctrl->sra &= ~FD_SRA_INTPEND;
769}
770
771static void fdctrl_raise_irq(fdctrl_t *fdctrl, uint8_t status0)
772{
773 if (!(fdctrl->sra & FD_SRA_INTPEND)) {
774#ifdef VBOX
775 PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 1);
776#else
777 qemu_set_irq(fdctrl->irq, 1);
778#endif
779 fdctrl->sra |= FD_SRA_INTPEND;
780 }
781 if (status0 & FD_SR0_SEEK) {
782 fdrive_t *cur_drv;
783
784 /* A seek clears the disk change line (if a disk is inserted). */
785 cur_drv = get_cur_drv(fdctrl);
786 if (cur_drv->max_track)
787 cur_drv->dsk_chg = false;
788 }
789
790 fdctrl->reset_sensei = 0;
791 fdctrl->status0 = status0;
792 FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
793}
794
795/* Reset controller */
796static void fdctrl_reset(fdctrl_t *fdctrl, int do_irq)
797{
798 int i;
799
800 FLOPPY_DPRINTF("reset controller\n");
801 fdctrl_reset_irq(fdctrl);
802 /* Initialise controller */
803 fdctrl->sra = 0;
804 fdctrl->srb = 0xc0;
805#ifdef VBOX
806 if (!fdctrl->drives[1].pDrvBlock)
807#else
808 if (!fdctrl->drives[1].bs)
809#endif
810 fdctrl->sra |= FD_SRA_nDRV2;
811 fdctrl->cur_drv = 0;
812 fdctrl->dor = FD_DOR_nRESET;
813 fdctrl->dor |= (fdctrl->dma_chann != 0xff) ? FD_DOR_DMAEN : 0;
814 fdctrl->msr = FD_MSR_RQM;
815 /* FIFO state */
816 fdctrl->data_pos = 0;
817 fdctrl->data_len = 0;
818 fdctrl->data_state = 0;
819 fdctrl->data_dir = FD_DIR_WRITE;
820 for (i = 0; i < MAX_FD; i++)
821 fd_recalibrate(&fdctrl->drives[i]);
822 fdctrl_reset_fifo(fdctrl);
823 if (do_irq) {
824 fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
825 fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
826 }
827}
828
829static inline fdrive_t *drv0(fdctrl_t *fdctrl)
830{
831 return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
832}
833
834static inline fdrive_t *drv1(fdctrl_t *fdctrl)
835{
836 if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
837 return &fdctrl->drives[1];
838 else
839 return &fdctrl->drives[0];
840}
841
842#if MAX_FD == 4
843static inline fdrive_t *drv2(fdctrl_t *fdctrl)
844{
845 if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
846 return &fdctrl->drives[2];
847 else
848 return &fdctrl->drives[1];
849}
850
851static inline fdrive_t *drv3(fdctrl_t *fdctrl)
852{
853 if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
854 return &fdctrl->drives[3];
855 else
856 return &fdctrl->drives[2];
857}
858#endif
859
860static fdrive_t *get_cur_drv(fdctrl_t *fdctrl)
861{
862 switch (fdctrl->cur_drv) {
863 case 0: return drv0(fdctrl);
864 case 1: return drv1(fdctrl);
865#if MAX_FD == 4
866 case 2: return drv2(fdctrl);
867 case 3: return drv3(fdctrl);
868#endif
869 default: return NULL;
870 }
871}
872
873/* Status A register : 0x00 (read-only) */
874static uint32_t fdctrl_read_statusA(fdctrl_t *fdctrl)
875{
876 uint32_t retval = fdctrl->sra;
877
878 FLOPPY_DPRINTF("status register A: 0x%02x\n", retval);
879
880 return retval;
881}
882
883/* Status B register : 0x01 (read-only) */
884static uint32_t fdctrl_read_statusB(fdctrl_t *fdctrl)
885{
886 uint32_t retval = fdctrl->srb;
887
888 FLOPPY_DPRINTF("status register B: 0x%02x\n", retval);
889
890 return retval;
891}
892
893/* Digital output register : 0x02 */
894static uint32_t fdctrl_read_dor(fdctrl_t *fdctrl)
895{
896 uint32_t retval = fdctrl->dor;
897
898 /* Selected drive */
899 retval |= fdctrl->cur_drv;
900 FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
901
902 return retval;
903}
904
905static void fdctrl_write_dor(fdctrl_t *fdctrl, uint32_t value)
906{
907 FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
908
909 /* Motors */
910 if (value & FD_DOR_MOTEN0)
911 fdctrl->srb |= FD_SRB_MTR0;
912 else
913 fdctrl->srb &= ~FD_SRB_MTR0;
914 if (value & FD_DOR_MOTEN1)
915 fdctrl->srb |= FD_SRB_MTR1;
916 else
917 fdctrl->srb &= ~FD_SRB_MTR1;
918
919 /* Drive */
920 if (value & 1)
921 fdctrl->srb |= FD_SRB_DR0;
922 else
923 fdctrl->srb &= ~FD_SRB_DR0;
924
925 /* Reset */
926 if (!(value & FD_DOR_nRESET)) {
927 if (fdctrl->dor & FD_DOR_nRESET) {
928 FLOPPY_DPRINTF("controller enter RESET state\n");
929 }
930 } else {
931 if (!(fdctrl->dor & FD_DOR_nRESET)) {
932 FLOPPY_DPRINTF("controller out of RESET state\n");
933 fdctrl_reset(fdctrl, 1);
934 fdctrl->dsr &= ~FD_DSR_PWRDOWN;
935 }
936 }
937 /* Selected drive */
938 fdctrl->cur_drv = value & FD_DOR_SELMASK;
939
940 fdctrl->dor = value;
941}
942
943/* Tape drive register : 0x03 */
944static uint32_t fdctrl_read_tape(fdctrl_t *fdctrl)
945{
946 uint32_t retval = fdctrl->tdr;
947
948 FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
949
950 return retval;
951}
952
953static void fdctrl_write_tape(fdctrl_t *fdctrl, uint32_t value)
954{
955 /* Reset mode */
956 if (!(fdctrl->dor & FD_DOR_nRESET)) {
957 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
958 return;
959 }
960 FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
961 /* Disk boot selection indicator */
962 fdctrl->tdr = value & FD_TDR_BOOTSEL;
963 /* Tape indicators: never allow */
964}
965
966/* Main status register : 0x04 (read) */
967static uint32_t fdctrl_read_main_status(fdctrl_t *fdctrl)
968{
969 uint32_t retval = fdctrl->msr;
970
971 fdctrl->dsr &= ~FD_DSR_PWRDOWN;
972 fdctrl->dor |= FD_DOR_nRESET;
973
974 FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
975
976 return retval;
977}
978
979/* Data select rate register : 0x04 (write) */
980static void fdctrl_write_rate(fdctrl_t *fdctrl, uint32_t value)
981{
982 /* Reset mode */
983 if (!(fdctrl->dor & FD_DOR_nRESET)) {
984 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
985 return;
986 }
987 FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
988 /* Reset: autoclear */
989 if (value & FD_DSR_SWRESET) {
990 fdctrl->dor &= ~FD_DOR_nRESET;
991 fdctrl_reset(fdctrl, 1);
992 fdctrl->dor |= FD_DOR_nRESET;
993 }
994 if (value & FD_DSR_PWRDOWN) {
995 fdctrl_reset(fdctrl, 1);
996 }
997 fdctrl->dsr = value;
998}
999
1000/* Configuration control register : 0x07 (write) */
1001static void fdctrl_write_ccr(fdctrl_t *fdctrl, uint32_t value)
1002{
1003 /* Reset mode */
1004 if (!(fdctrl->dor & FD_DOR_nRESET)) {
1005 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
1006 return;
1007 }
1008 FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);
1009
1010 /* Only the rate selection bits used in AT mode, and we
1011 * store those in the DSR.
1012 */
1013 fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) | (value & FD_DSR_DRATEMASK);
1014}
1015
1016static int fdctrl_media_changed(fdrive_t *drv)
1017{
1018#ifdef VBOX
1019 return drv->dsk_chg;
1020#else
1021 int ret;
1022
1023 if (!drv->bs)
1024 return 0;
1025 ret = bdrv_media_changed(drv->bs);
1026 if (ret) {
1027 fd_revalidate(drv);
1028 }
1029 return ret;
1030#endif
1031}
1032
1033/* Digital input register : 0x07 (read-only) */
1034static uint32_t fdctrl_read_dir(fdctrl_t *fdctrl)
1035{
1036 uint32_t retval = 0;
1037
1038#ifdef VBOX
1039 if (fdctrl_media_changed(get_cur_drv(fdctrl)))
1040#else
1041 if (fdctrl_media_changed(drv0(fdctrl))
1042 || fdctrl_media_changed(drv1(fdctrl))
1043#if MAX_FD == 4
1044 || fdctrl_media_changed(drv2(fdctrl))
1045 || fdctrl_media_changed(drv3(fdctrl))
1046#endif
1047 )
1048#endif
1049 retval |= FD_DIR_DSKCHG;
1050 if (retval != 0)
1051 FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
1052
1053 return retval;
1054}
1055
1056/* FIFO state control */
1057static void fdctrl_reset_fifo(fdctrl_t *fdctrl)
1058{
1059 fdctrl->data_dir = FD_DIR_WRITE;
1060 fdctrl->data_pos = 0;
1061 fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
1062}
1063
1064/* Set FIFO status for the host to read */
1065static void fdctrl_set_fifo(fdctrl_t *fdctrl, int fifo_len, int do_irq)
1066{
1067 fdctrl->data_dir = FD_DIR_READ;
1068 fdctrl->data_len = fifo_len;
1069 fdctrl->data_pos = 0;
1070 fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
1071 if (do_irq)
1072 fdctrl_raise_irq(fdctrl, 0x00);
1073}
1074
1075/* Set an error: unimplemented/unknown command */
1076static void fdctrl_unimplemented(fdctrl_t *fdctrl, int direction)
1077{
1078 FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]);
1079 fdctrl->fifo[0] = FD_SR0_INVCMD;
1080 fdctrl_set_fifo(fdctrl, 1, 0);
1081}
1082
1083/* Seek to next sector */
1084static int fdctrl_seek_to_next_sect(fdctrl_t *fdctrl, fdrive_t *cur_drv)
1085{
1086 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
1087 cur_drv->head, cur_drv->track, cur_drv->sect,
1088 fd_sector(cur_drv));
1089 /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
1090 error in fact */
1091 if (cur_drv->sect >= cur_drv->last_sect ||
1092 cur_drv->sect == fdctrl->eot) {
1093 cur_drv->sect = 1;
1094 if (FD_MULTI_TRACK(fdctrl->data_state)) {
1095 if (cur_drv->head == 0 &&
1096 (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1097 cur_drv->head = 1;
1098 } else {
1099 cur_drv->head = 0;
1100 cur_drv->track++;
1101 if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
1102 return 0;
1103 }
1104 } else {
1105 cur_drv->track++;
1106 return 0;
1107 }
1108 FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
1109 cur_drv->head, cur_drv->track,
1110 cur_drv->sect, fd_sector(cur_drv));
1111 } else {
1112 cur_drv->sect++;
1113 }
1114 return 1;
1115}
1116
1117/* Callback for transfer end (stop or abort) */
1118static void fdctrl_stop_transfer(fdctrl_t *fdctrl, uint8_t status0,
1119 uint8_t status1, uint8_t status2)
1120{
1121 fdrive_t *cur_drv;
1122
1123 cur_drv = get_cur_drv(fdctrl);
1124 FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
1125 status0, status1, status2,
1126 status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl));
1127 fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1128 fdctrl->fifo[1] = status1;
1129 fdctrl->fifo[2] = status2;
1130 fdctrl->fifo[3] = cur_drv->track;
1131 fdctrl->fifo[4] = cur_drv->head;
1132 fdctrl->fifo[5] = cur_drv->sect;
1133 fdctrl->fifo[6] = FD_SECTOR_SC;
1134 fdctrl->data_dir = FD_DIR_READ;
1135 if (!(fdctrl->msr & FD_MSR_NONDMA)) {
1136#ifdef VBOX
1137 PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 0);
1138#else
1139 DMA_release_DREQ(fdctrl->dma_chann);
1140#endif
1141 }
1142 fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
1143 fdctrl->msr &= ~FD_MSR_NONDMA;
1144 fdctrl_set_fifo(fdctrl, 7, 1);
1145}
1146
1147/* Prepare a data transfer (either DMA or FIFO) */
1148static void fdctrl_start_transfer(fdctrl_t *fdctrl, int direction)
1149{
1150 fdrive_t *cur_drv;
1151 uint8_t kh, kt, ks;
1152 int did_seek = 0;
1153
1154 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1155 cur_drv = get_cur_drv(fdctrl);
1156 kt = fdctrl->fifo[2];
1157 kh = fdctrl->fifo[3];
1158 ks = fdctrl->fifo[4];
1159 FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
1160 GET_CUR_DRV(fdctrl), kh, kt, ks,
1161 fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
1162 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1163 case 2:
1164 /* sect too big */
1165 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1166 fdctrl->fifo[3] = kt;
1167 fdctrl->fifo[4] = kh;
1168 fdctrl->fifo[5] = ks;
1169 return;
1170 case 3:
1171 /* track too big */
1172 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1173 fdctrl->fifo[3] = kt;
1174 fdctrl->fifo[4] = kh;
1175 fdctrl->fifo[5] = ks;
1176 return;
1177 case 4:
1178 /* No seek enabled */
1179 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1180 fdctrl->fifo[3] = kt;
1181 fdctrl->fifo[4] = kh;
1182 fdctrl->fifo[5] = ks;
1183 return;
1184 case 1:
1185 did_seek = 1;
1186 break;
1187 default:
1188 break;
1189 }
1190 /* Check the data rate. If the programmed data rate does not match
1191 * the currently inserted medium, the operation has to fail.
1192 */
1193#ifdef VBOX
1194 if ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
1195 FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
1196 fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
1197 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
1198 fdctrl->fifo[3] = kt;
1199 fdctrl->fifo[4] = kh;
1200 fdctrl->fifo[5] = ks;
1201 return;
1202 }
1203#endif
1204 /* Set the FIFO state */
1205 fdctrl->data_dir = direction;
1206 fdctrl->data_pos = 0;
1207 fdctrl->msr |= FD_MSR_CMDBUSY;
1208 if (fdctrl->fifo[0] & 0x80)
1209 fdctrl->data_state |= FD_STATE_MULTI;
1210 else
1211 fdctrl->data_state &= ~FD_STATE_MULTI;
1212 if (did_seek)
1213 fdctrl->data_state |= FD_STATE_SEEK;
1214 else
1215 fdctrl->data_state &= ~FD_STATE_SEEK;
1216 if (fdctrl->fifo[5] == 00) {
1217 fdctrl->data_len = fdctrl->fifo[8];
1218 } else {
1219 int tmp;
1220 fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
1221 tmp = (fdctrl->fifo[6] - ks + 1);
1222 if (fdctrl->fifo[0] & 0x80)
1223 tmp += fdctrl->fifo[6];
1224 fdctrl->data_len *= tmp;
1225 }
1226 fdctrl->eot = fdctrl->fifo[6];
1227 if (fdctrl->dor & FD_DOR_DMAEN) {
1228 int dma_mode;
1229 /* DMA transfer are enabled. Check if DMA channel is well programmed */
1230#ifndef VBOX
1231 dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
1232#else
1233 dma_mode = PDMDevHlpDMAGetChannelMode (fdctrl->pDevIns, fdctrl->dma_chann);
1234#endif
1235 dma_mode = (dma_mode >> 2) & 3;
1236 FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
1237 dma_mode, direction,
1238 (128 << fdctrl->fifo[5]) *
1239 (cur_drv->last_sect - ks + 1), fdctrl->data_len);
1240 if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
1241 direction == FD_DIR_SCANH) && dma_mode == 0) ||
1242 (direction == FD_DIR_WRITE && dma_mode == 2) ||
1243 (direction == FD_DIR_READ && dma_mode == 1)) {
1244 /* No access is allowed until DMA transfer has completed */
1245 fdctrl->msr &= ~FD_MSR_RQM;
1246 /* Now, we just have to wait for the DMA controller to
1247 * recall us...
1248 */
1249#ifndef VBOX
1250 DMA_hold_DREQ(fdctrl->dma_chann);
1251 DMA_schedule(fdctrl->dma_chann);
1252#else
1253 PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 1);
1254 PDMDevHlpDMASchedule (fdctrl->pDevIns);
1255#endif
1256 return;
1257 } else {
1258 FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
1259 }
1260 }
1261 FLOPPY_DPRINTF("start non-DMA transfer\n");
1262 fdctrl->msr |= FD_MSR_NONDMA;
1263 if (direction != FD_DIR_WRITE)
1264 fdctrl->msr |= FD_MSR_DIO;
1265 /* IO based transfer: calculate len */
1266 fdctrl_raise_irq(fdctrl, 0x00);
1267
1268 return;
1269}
1270
1271/* Prepare a transfer of deleted data */
1272static void fdctrl_start_transfer_del(fdctrl_t *fdctrl, int direction)
1273{
1274 FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n");
1275
1276 /* We don't handle deleted data,
1277 * so we don't return *ANYTHING*
1278 */
1279 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1280}
1281
1282#ifdef VBOX
1283/* Block driver read/write wrappers. */
1284
1285static int blk_write(fdrive_t *drv, int64_t sector_num, const uint8_t *buf, int nb_sectors)
1286{
1287 int rc;
1288
1289 drv->Led.Asserted.s.fWriting = drv->Led.Actual.s.fWriting = 1;
1290
1291 rc = drv->pDrvBlock->pfnWrite(drv->pDrvBlock, sector_num * FD_SECTOR_LEN,
1292 buf, nb_sectors * FD_SECTOR_LEN);
1293
1294 drv->Led.Actual.s.fWriting = 0;
1295 if (RT_FAILURE(rc))
1296 AssertMsgFailed(("Floppy: Failure to read sector %d. rc=%Rrc", sector_num, rc));
1297
1298 return rc;
1299}
1300
1301static int blk_read(fdrive_t *drv, int64_t sector_num, uint8_t *buf, int nb_sectors)
1302{
1303 int rc;
1304
1305 drv->Led.Asserted.s.fReading = drv->Led.Actual.s.fReading = 1;
1306
1307 rc = drv->pDrvBlock->pfnRead(drv->pDrvBlock, sector_num * FD_SECTOR_LEN,
1308 buf, nb_sectors * FD_SECTOR_LEN);
1309
1310 drv->Led.Actual.s.fReading = 0;
1311
1312 if (RT_FAILURE(rc))
1313 AssertMsgFailed(("Floppy: Failure to read sector %d. rc=%Rrc", sector_num, rc));
1314
1315 return rc;
1316}
1317
1318#endif
1319
1320/* handlers for DMA transfers */
1321#ifdef VBOX
1322static DECLCALLBACK(uint32_t) fdctrl_transfer_handler (PPDMDEVINS pDevIns,
1323 void *opaque,
1324 unsigned nchan,
1325 uint32_t dma_pos,
1326 uint32_t dma_len)
1327#else
1328static int fdctrl_transfer_handler (void *opaque, int nchan,
1329 int dma_pos, int dma_len)
1330#endif
1331{
1332 fdctrl_t *fdctrl;
1333 fdrive_t *cur_drv;
1334#ifdef VBOX
1335 int rc;
1336 uint32_t len, start_pos, rel_pos;
1337#else
1338 int len, start_pos, rel_pos;
1339#endif
1340 uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
1341
1342 fdctrl = opaque;
1343 if (fdctrl->msr & FD_MSR_RQM) {
1344 FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
1345 return 0;
1346 }
1347 cur_drv = get_cur_drv(fdctrl);
1348 if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
1349 fdctrl->data_dir == FD_DIR_SCANH)
1350 status2 = FD_SR2_SNS;
1351 if (dma_len > fdctrl->data_len)
1352 dma_len = fdctrl->data_len;
1353#ifndef VBOX
1354 if (cur_drv->bs == NULL)
1355#else /* !VBOX */
1356 if (cur_drv->pDrvBlock == NULL)
1357#endif
1358 {
1359 if (fdctrl->data_dir == FD_DIR_WRITE)
1360 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1361 else
1362 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1363 len = 0;
1364 goto transfer_error;
1365 }
1366 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1367 for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
1368 len = dma_len - fdctrl->data_pos;
1369 if (len + rel_pos > FD_SECTOR_LEN)
1370 len = FD_SECTOR_LEN - rel_pos;
1371 FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
1372 "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
1373 fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
1374 cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
1375 fd_sector(cur_drv) * FD_SECTOR_LEN);
1376 if (fdctrl->data_dir != FD_DIR_WRITE ||
1377 len < FD_SECTOR_LEN || rel_pos != 0) {
1378 /* READ & SCAN commands and realign to a sector for WRITE */
1379#ifdef VBOX
1380 rc = blk_read(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
1381 if (RT_FAILURE(rc))
1382#else
1383 if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1384 fdctrl->fifo, 1) < 0)
1385#endif
1386 {
1387 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
1388 fd_sector(cur_drv));
1389 /* Sure, image size is too small... */
1390 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1391 }
1392 }
1393 switch (fdctrl->data_dir) {
1394 case FD_DIR_READ:
1395 /* READ commands */
1396#ifdef VBOX
1397 {
1398 uint32_t read;
1399 int rc2 = PDMDevHlpDMAWriteMemory(fdctrl->pDevIns, nchan,
1400 fdctrl->fifo + rel_pos,
1401 fdctrl->data_pos,
1402 len, &read);
1403 AssertMsgRC (rc2, ("DMAWriteMemory -> %Rrc\n", rc2));
1404 }
1405#else
1406 DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
1407 fdctrl->data_pos, len);
1408#endif
1409/* cpu_physical_memory_write(addr + fdctrl->data_pos, */
1410/* fdctrl->fifo + rel_pos, len); */
1411 break;
1412 case FD_DIR_WRITE:
1413 /* WRITE commands */
1414#ifdef VBOX
1415 if (cur_drv->ro)
1416 {
1417 /* Handle readonly medium early, no need to do DMA, touch the
1418 * LED or attempt any writes. A real floppy doesn't attempt
1419 * to write to readonly media either. */
1420 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
1421 0x00);
1422 goto transfer_error;
1423 }
1424
1425 {
1426 uint32_t written;
1427 int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan,
1428 fdctrl->fifo + rel_pos,
1429 fdctrl->data_pos,
1430 len, &written);
1431 AssertMsgRC (rc2, ("DMAReadMemory -> %Rrc\n", rc2));
1432 }
1433
1434 rc = blk_write(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
1435 if (RT_FAILURE(rc))
1436#else
1437 DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
1438 fdctrl->data_pos, len);
1439 if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1440 fdctrl->fifo, 1) < 0)
1441#endif
1442 {
1443 FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
1444 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1445 goto transfer_error;
1446 }
1447 break;
1448 default:
1449 /* SCAN commands */
1450 {
1451 uint8_t tmpbuf[FD_SECTOR_LEN];
1452 int ret;
1453#ifdef VBOX
1454 uint32_t read;
1455 int rc2 = PDMDevHlpDMAReadMemory (fdctrl->pDevIns, nchan, tmpbuf,
1456 fdctrl->data_pos, len, &read);
1457 AssertMsg (RT_SUCCESS (rc2), ("DMAReadMemory -> %Rrc2\n", rc2));
1458#else
1459 DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
1460#endif
1461 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
1462 if (ret == 0) {
1463 status2 = FD_SR2_SEH;
1464 goto end_transfer;
1465 }
1466 if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
1467 (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
1468 status2 = 0x00;
1469 goto end_transfer;
1470 }
1471 }
1472 break;
1473 }
1474 fdctrl->data_pos += len;
1475 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1476 if (rel_pos == 0) {
1477 /* Seek to next sector */
1478 if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
1479 break;
1480 }
1481 }
1482end_transfer:
1483 len = fdctrl->data_pos - start_pos;
1484 FLOPPY_DPRINTF("end transfer %d %d %d\n",
1485 fdctrl->data_pos, len, fdctrl->data_len);
1486 if (fdctrl->data_dir == FD_DIR_SCANE ||
1487 fdctrl->data_dir == FD_DIR_SCANL ||
1488 fdctrl->data_dir == FD_DIR_SCANH)
1489 status2 = FD_SR2_SEH;
1490 if (FD_DID_SEEK(fdctrl->data_state))
1491 status0 |= FD_SR0_SEEK;
1492 fdctrl->data_len -= len;
1493 fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1494transfer_error:
1495
1496 return len;
1497}
1498
1499/* Data register : 0x05 */
1500static uint32_t fdctrl_read_data(fdctrl_t *fdctrl)
1501{
1502 fdrive_t *cur_drv;
1503 uint32_t retval = 0;
1504 unsigned pos;
1505#ifdef VBOX
1506 int rc;
1507#endif
1508
1509 cur_drv = get_cur_drv(fdctrl);
1510 fdctrl->dsr &= ~FD_DSR_PWRDOWN;
1511 if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
1512 FLOPPY_ERROR("controller not ready for reading\n");
1513 return 0;
1514 }
1515 pos = fdctrl->data_pos;
1516 if (fdctrl->msr & FD_MSR_NONDMA) {
1517 pos %= FD_SECTOR_LEN;
1518 if (pos == 0) {
1519 if (fdctrl->data_pos != 0)
1520 if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
1521 FLOPPY_DPRINTF("error seeking to next sector %d\n",
1522 fd_sector(cur_drv));
1523 return 0;
1524 }
1525#ifdef VBOX
1526 rc = blk_read(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
1527 if (RT_FAILURE(rc))
1528#else
1529 if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0)
1530#endif
1531 {
1532 FLOPPY_DPRINTF("error getting sector %d\n",
1533 fd_sector(cur_drv));
1534 /* Sure, image size is too small... */
1535 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1536 }
1537 }
1538 }
1539 retval = fdctrl->fifo[pos];
1540 if (++fdctrl->data_pos == fdctrl->data_len) {
1541 fdctrl->data_pos = 0;
1542 /* Switch from transfer mode to status mode
1543 * then from status mode to command mode
1544 */
1545 if (fdctrl->msr & FD_MSR_NONDMA) {
1546 fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1547 } else {
1548 fdctrl_reset_fifo(fdctrl);
1549 fdctrl_reset_irq(fdctrl);
1550 }
1551 }
1552 FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
1553
1554 return retval;
1555}
1556
1557static void fdctrl_format_sector(fdctrl_t *fdctrl)
1558{
1559 fdrive_t *cur_drv;
1560 uint8_t kh, kt, ks;
1561#ifdef VBOX
1562 int ok = 0, rc;
1563#endif
1564
1565 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1566 cur_drv = get_cur_drv(fdctrl);
1567 kt = fdctrl->fifo[6];
1568 kh = fdctrl->fifo[7];
1569 ks = fdctrl->fifo[8];
1570 FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
1571 GET_CUR_DRV(fdctrl), kh, kt, ks,
1572 fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
1573 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
1574 case 2:
1575 /* sect too big */
1576 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1577 fdctrl->fifo[3] = kt;
1578 fdctrl->fifo[4] = kh;
1579 fdctrl->fifo[5] = ks;
1580 return;
1581 case 3:
1582 /* track too big */
1583 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
1584 fdctrl->fifo[3] = kt;
1585 fdctrl->fifo[4] = kh;
1586 fdctrl->fifo[5] = ks;
1587 return;
1588 case 4:
1589 /* No seek enabled */
1590 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
1591 fdctrl->fifo[3] = kt;
1592 fdctrl->fifo[4] = kh;
1593 fdctrl->fifo[5] = ks;
1594 return;
1595 case 1:
1596 fdctrl->data_state |= FD_STATE_SEEK;
1597 break;
1598 default:
1599 break;
1600 }
1601 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1602#ifdef VBOX
1603 if (cur_drv->pDrvBlock) {
1604 rc = blk_write(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
1605 if (RT_FAILURE (rc)) {
1606 FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
1607 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1608 } else {
1609 ok = 1;
1610 }
1611 }
1612 if (ok) {
1613#else
1614 if (cur_drv->bs == NULL ||
1615 bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
1616 FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
1617 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
1618 } else {
1619#endif
1620 if (cur_drv->sect == cur_drv->last_sect) {
1621 fdctrl->data_state &= ~FD_STATE_FORMAT;
1622 /* Last sector done */
1623 if (FD_DID_SEEK(fdctrl->data_state))
1624 fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
1625 else
1626 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1627 } else {
1628 /* More to do */
1629 fdctrl->data_pos = 0;
1630 fdctrl->data_len = 4;
1631 }
1632 }
1633}
1634
1635static void fdctrl_handle_lock(fdctrl_t *fdctrl, int direction)
1636{
1637 fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
1638 fdctrl->fifo[0] = fdctrl->lock << 4;
1639 fdctrl_set_fifo(fdctrl, 1, 0);
1640}
1641
1642static void fdctrl_handle_dumpreg(fdctrl_t *fdctrl, int direction)
1643{
1644 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1645
1646 /* Drives position */
1647 fdctrl->fifo[0] = drv0(fdctrl)->track;
1648 fdctrl->fifo[1] = drv1(fdctrl)->track;
1649#if MAX_FD == 4
1650 fdctrl->fifo[2] = drv2(fdctrl)->track;
1651 fdctrl->fifo[3] = drv3(fdctrl)->track;
1652#else
1653 fdctrl->fifo[2] = 0;
1654 fdctrl->fifo[3] = 0;
1655#endif
1656 /* timers */
1657 fdctrl->fifo[4] = fdctrl->timer0;
1658 fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
1659 fdctrl->fifo[6] = cur_drv->last_sect;
1660 fdctrl->fifo[7] = (fdctrl->lock << 7) |
1661 (cur_drv->perpendicular << 2);
1662 fdctrl->fifo[8] = fdctrl->config;
1663 fdctrl->fifo[9] = fdctrl->precomp_trk;
1664 fdctrl_set_fifo(fdctrl, 10, 0);
1665}
1666
1667static void fdctrl_handle_version(fdctrl_t *fdctrl, int direction)
1668{
1669 /* Controller's version */
1670 fdctrl->fifo[0] = fdctrl->version;
1671 fdctrl_set_fifo(fdctrl, 1, 0);
1672}
1673
1674static void fdctrl_handle_partid(fdctrl_t *fdctrl, int direction)
1675{
1676 fdctrl->fifo[0] = 0x01; /* Stepping 1 */
1677 fdctrl_set_fifo(fdctrl, 1, 0);
1678}
1679
1680static void fdctrl_handle_restore(fdctrl_t *fdctrl, int direction)
1681{
1682 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1683
1684 /* Drives position */
1685 drv0(fdctrl)->track = fdctrl->fifo[3];
1686 drv1(fdctrl)->track = fdctrl->fifo[4];
1687#if MAX_FD == 4
1688 drv2(fdctrl)->track = fdctrl->fifo[5];
1689 drv3(fdctrl)->track = fdctrl->fifo[6];
1690#endif
1691 /* timers */
1692 fdctrl->timer0 = fdctrl->fifo[7];
1693 fdctrl->timer1 = fdctrl->fifo[8];
1694 cur_drv->last_sect = fdctrl->fifo[9];
1695 fdctrl->lock = fdctrl->fifo[10] >> 7;
1696 cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
1697 fdctrl->config = fdctrl->fifo[11];
1698 fdctrl->precomp_trk = fdctrl->fifo[12];
1699 fdctrl->pwrd = fdctrl->fifo[13];
1700 fdctrl_reset_fifo(fdctrl);
1701}
1702
1703static void fdctrl_handle_save(fdctrl_t *fdctrl, int direction)
1704{
1705 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1706
1707 fdctrl->fifo[0] = 0;
1708 fdctrl->fifo[1] = 0;
1709 /* Drives position */
1710 fdctrl->fifo[2] = drv0(fdctrl)->track;
1711 fdctrl->fifo[3] = drv1(fdctrl)->track;
1712#if MAX_FD == 4
1713 fdctrl->fifo[4] = drv2(fdctrl)->track;
1714 fdctrl->fifo[5] = drv3(fdctrl)->track;
1715#else
1716 fdctrl->fifo[4] = 0;
1717 fdctrl->fifo[5] = 0;
1718#endif
1719 /* timers */
1720 fdctrl->fifo[6] = fdctrl->timer0;
1721 fdctrl->fifo[7] = fdctrl->timer1;
1722 fdctrl->fifo[8] = cur_drv->last_sect;
1723 fdctrl->fifo[9] = (fdctrl->lock << 7) |
1724 (cur_drv->perpendicular << 2);
1725 fdctrl->fifo[10] = fdctrl->config;
1726 fdctrl->fifo[11] = fdctrl->precomp_trk;
1727 fdctrl->fifo[12] = fdctrl->pwrd;
1728 fdctrl->fifo[13] = 0;
1729 fdctrl->fifo[14] = 0;
1730 fdctrl_set_fifo(fdctrl, 15, 0);
1731}
1732
1733static void fdctrl_handle_readid(fdctrl_t *fdctrl, int direction)
1734{
1735 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1736
1737 /* XXX: should set main status register to busy */
1738 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
1739#ifdef VBOX
1740 TMTimerSetMillies(fdctrl->result_timer, 1000 / 50);
1741#else
1742 qemu_mod_timer(fdctrl->result_timer,
1743 qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 50));
1744#endif
1745}
1746
1747static void fdctrl_handle_format_track(fdctrl_t *fdctrl, int direction)
1748{
1749 fdrive_t *cur_drv;
1750
1751 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1752 cur_drv = get_cur_drv(fdctrl);
1753 fdctrl->data_state |= FD_STATE_FORMAT;
1754 if (fdctrl->fifo[0] & 0x80)
1755 fdctrl->data_state |= FD_STATE_MULTI;
1756 else
1757 fdctrl->data_state &= ~FD_STATE_MULTI;
1758 fdctrl->data_state &= ~FD_STATE_SEEK;
1759 cur_drv->bps =
1760 fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
1761#if 0
1762 cur_drv->last_sect =
1763 cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
1764 fdctrl->fifo[3] / 2;
1765#else
1766 cur_drv->last_sect = fdctrl->fifo[3];
1767#endif
1768 /* TODO: implement format using DMA expected by the Bochs BIOS
1769 * and Linux fdformat (read 3 bytes per sector via DMA and fill
1770 * the sector with the specified fill byte
1771 */
1772 fdctrl->data_state &= ~FD_STATE_FORMAT;
1773 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1774}
1775
1776static void fdctrl_handle_specify(fdctrl_t *fdctrl, int direction)
1777{
1778 fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
1779 fdctrl->timer1 = fdctrl->fifo[2] >> 1;
1780 if (fdctrl->fifo[2] & 1)
1781 fdctrl->dor &= ~FD_DOR_DMAEN;
1782 else
1783 fdctrl->dor |= FD_DOR_DMAEN;
1784 /* No result back */
1785 fdctrl_reset_fifo(fdctrl);
1786}
1787
1788static void fdctrl_handle_sense_drive_status(fdctrl_t *fdctrl, int direction)
1789{
1790 fdrive_t *cur_drv;
1791
1792 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1793 cur_drv = get_cur_drv(fdctrl);
1794 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
1795 /* 1 Byte status back */
1796 fdctrl->fifo[0] = (cur_drv->ro << 6) |
1797 (cur_drv->track == 0 ? 0x10 : 0x00) |
1798 (cur_drv->head << 2) |
1799 GET_CUR_DRV(fdctrl) |
1800 0x28;
1801 fdctrl_set_fifo(fdctrl, 1, 0);
1802}
1803
1804static void fdctrl_handle_recalibrate(fdctrl_t *fdctrl, int direction)
1805{
1806 fdrive_t *cur_drv;
1807
1808 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1809 cur_drv = get_cur_drv(fdctrl);
1810 fd_recalibrate(cur_drv);
1811 fdctrl_reset_fifo(fdctrl);
1812 /* Raise Interrupt */
1813 fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1814}
1815
1816static void fdctrl_handle_sense_interrupt_status(fdctrl_t *fdctrl, int direction)
1817{
1818 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1819
1820 if(fdctrl->reset_sensei > 0) {
1821 fdctrl->fifo[0] =
1822 FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
1823 fdctrl->reset_sensei--;
1824 } else {
1825 /* XXX: status0 handling is broken for read/write
1826 commands, so we do this hack. It should be suppressed
1827 ASAP */
1828 fdctrl->fifo[0] =
1829 FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1830 }
1831
1832 fdctrl->fifo[1] = cur_drv->track;
1833 fdctrl_set_fifo(fdctrl, 2, 0);
1834 fdctrl_reset_irq(fdctrl);
1835 fdctrl->status0 = FD_SR0_RDYCHG;
1836}
1837
1838static void fdctrl_handle_seek(fdctrl_t *fdctrl, int direction)
1839{
1840 fdrive_t *cur_drv;
1841
1842 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1843 cur_drv = get_cur_drv(fdctrl);
1844 fdctrl_reset_fifo(fdctrl);
1845#ifdef VBOX
1846 /* The seek command just sends step pulses to the drive and doesn't care if
1847 * there's a medium inserted or if it's banging the head against the drive.
1848 */
1849 cur_drv->track = fdctrl->fifo[2];
1850 /* Raise Interrupt */
1851 fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1852#else
1853 if (fdctrl->fifo[2] > cur_drv->max_track) {
1854 fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
1855 } else {
1856 cur_drv->track = fdctrl->fifo[2];
1857 /* Raise Interrupt */
1858 fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1859 }
1860#endif
1861}
1862
1863static void fdctrl_handle_perpendicular_mode(fdctrl_t *fdctrl, int direction)
1864{
1865 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1866
1867 if (fdctrl->fifo[1] & 0x80)
1868 cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
1869 /* No result back */
1870 fdctrl_reset_fifo(fdctrl);
1871}
1872
1873static void fdctrl_handle_configure(fdctrl_t *fdctrl, int direction)
1874{
1875 fdctrl->config = fdctrl->fifo[2];
1876 fdctrl->precomp_trk = fdctrl->fifo[3];
1877 /* No result back */
1878 fdctrl_reset_fifo(fdctrl);
1879}
1880
1881static void fdctrl_handle_powerdown_mode(fdctrl_t *fdctrl, int direction)
1882{
1883 fdctrl->pwrd = fdctrl->fifo[1];
1884 fdctrl->fifo[0] = fdctrl->fifo[1];
1885 fdctrl_set_fifo(fdctrl, 1, 0);
1886}
1887
1888static void fdctrl_handle_option(fdctrl_t *fdctrl, int direction)
1889{
1890 /* No result back */
1891 fdctrl_reset_fifo(fdctrl);
1892}
1893
1894static void fdctrl_handle_drive_specification_command(fdctrl_t *fdctrl, int direction)
1895{
1896 fdrive_t *cur_drv = get_cur_drv(fdctrl);
1897
1898 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
1899 /* Command parameters done */
1900 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
1901 fdctrl->fifo[0] = fdctrl->fifo[1];
1902 fdctrl->fifo[2] = 0;
1903 fdctrl->fifo[3] = 0;
1904 fdctrl_set_fifo(fdctrl, 4, 0);
1905 } else {
1906 fdctrl_reset_fifo(fdctrl);
1907 }
1908 } else if (fdctrl->data_len > 7) {
1909 /* ERROR */
1910 fdctrl->fifo[0] = 0x80 |
1911 (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
1912 fdctrl_set_fifo(fdctrl, 1, 0);
1913 }
1914}
1915
1916static void fdctrl_handle_relative_seek_out(fdctrl_t *fdctrl, int direction)
1917{
1918 fdrive_t *cur_drv;
1919
1920 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1921 cur_drv = get_cur_drv(fdctrl);
1922 if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
1923 cur_drv->track = cur_drv->max_track - 1;
1924 } else {
1925 cur_drv->track += fdctrl->fifo[2];
1926 }
1927 fdctrl_reset_fifo(fdctrl);
1928 /* Raise Interrupt */
1929 fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1930}
1931
1932static void fdctrl_handle_relative_seek_in(fdctrl_t *fdctrl, int direction)
1933{
1934 fdrive_t *cur_drv;
1935
1936 SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
1937 cur_drv = get_cur_drv(fdctrl);
1938 if (fdctrl->fifo[2] > cur_drv->track) {
1939 cur_drv->track = 0;
1940 } else {
1941 cur_drv->track -= fdctrl->fifo[2];
1942 }
1943 fdctrl_reset_fifo(fdctrl);
1944 /* Raise Interrupt */
1945 fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
1946}
1947
1948static const struct {
1949 uint8_t value;
1950 uint8_t mask;
1951 const char* name;
1952 int parameters;
1953 void (*handler)(fdctrl_t *fdctrl, int direction);
1954 int direction;
1955} handlers[] = {
1956 { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
1957 { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
1958 { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
1959 { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
1960 { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
1961 { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
1962 { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
1963 { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
1964 { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
1965 { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
1966 { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
1967 { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
1968 { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
1969 { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
1970 { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
1971 { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
1972 { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
1973 { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
1974 { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
1975 { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
1976 { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
1977 { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
1978 { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
1979 { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
1980 { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
1981 { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
1982 { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
1983 { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
1984 { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
1985 { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
1986 { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
1987 { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
1988};
1989/* Associate command to an index in the 'handlers' array */
1990static uint8_t command_to_handler[256];
1991
1992static void fdctrl_write_data(fdctrl_t *fdctrl, uint32_t value)
1993{
1994 fdrive_t *cur_drv;
1995 int pos;
1996
1997 cur_drv = get_cur_drv(fdctrl);
1998 /* Reset mode */
1999 if (!(fdctrl->dor & FD_DOR_nRESET)) {
2000 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
2001 return;
2002 }
2003 if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
2004 FLOPPY_ERROR("controller not ready for writing\n");
2005 return;
2006 }
2007 fdctrl->dsr &= ~FD_DSR_PWRDOWN;
2008 /* Is it write command time ? */
2009 if (fdctrl->msr & FD_MSR_NONDMA) {
2010 /* FIFO data write */
2011 pos = fdctrl->data_pos++;
2012 pos %= FD_SECTOR_LEN;
2013 fdctrl->fifo[pos] = value;
2014 if (pos == FD_SECTOR_LEN - 1 ||
2015 fdctrl->data_pos == fdctrl->data_len) {
2016#ifdef VBOX
2017 blk_write(cur_drv, fd_sector(cur_drv), fdctrl->fifo, 1);
2018#else
2019 bdrv_write(cur_drv->bs, fd_sector(cur_drv),
2020 fdctrl->fifo, 1);
2021#endif
2022 }
2023 /* Switch from transfer mode to status mode
2024 * then from status mode to command mode
2025 */
2026 if (fdctrl->data_pos == fdctrl->data_len)
2027 fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
2028 return;
2029 }
2030 if (fdctrl->data_pos == 0) {
2031 /* Command */
2032 pos = command_to_handler[value & 0xff];
2033 FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
2034 fdctrl->data_len = handlers[pos].parameters + 1;
2035 fdctrl->msr |= FD_MSR_CMDBUSY;
2036 }
2037
2038 FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
2039 fdctrl->fifo[fdctrl->data_pos++] = value;
2040 if (fdctrl->data_pos == fdctrl->data_len) {
2041 /* We now have all parameters
2042 * and will be able to treat the command
2043 */
2044 if (fdctrl->data_state & FD_STATE_FORMAT) {
2045 fdctrl_format_sector(fdctrl);
2046 return;
2047 }
2048
2049 pos = command_to_handler[fdctrl->fifo[0] & 0xff];
2050 FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
2051 (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
2052 }
2053}
2054
2055static void fdctrl_result_timer(void *opaque)
2056{
2057 fdctrl_t *fdctrl = opaque;
2058 fdrive_t *cur_drv = get_cur_drv(fdctrl);
2059
2060 /* Pretend we are spinning.
2061 * This is needed for Coherent, which uses READ ID to check for
2062 * sector interleaving.
2063 */
2064 if (cur_drv->last_sect != 0) {
2065 cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
2066 }
2067 /* READ_ID can't automatically succeed! */
2068#ifdef VBOX
2069 if (/* !cur_drv->fMediaPresent || */
2070 ((fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate)) {
2071 FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
2072 fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
2073 fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
2074 }
2075 else
2076#endif
2077 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
2078}
2079
2080#ifdef VBOX
2081static DECLCALLBACK(void) fdc_timer (PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2082{
2083 fdctrl_t *fdctrl = (fdctrl_t *)pvUser;
2084 fdctrl_result_timer (fdctrl);
2085}
2086
2087static DECLCALLBACK(int) fdc_io_write (PPDMDEVINS pDevIns,
2088 void *pvUser,
2089 RTIOPORT Port,
2090 uint32_t u32,
2091 unsigned cb)
2092{
2093 if (cb == 1) {
2094 fdctrl_write (pvUser, Port & 7, u32);
2095 }
2096 else {
2097 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2098 }
2099 return VINF_SUCCESS;
2100}
2101
2102static DECLCALLBACK(int) fdc_io_read (PPDMDEVINS pDevIns,
2103 void *pvUser,
2104 RTIOPORT Port,
2105 uint32_t *pu32,
2106 unsigned cb)
2107{
2108 if (cb == 1) {
2109 *pu32 = fdctrl_read (pvUser, Port & 7);
2110 return VINF_SUCCESS;
2111 }
2112 else {
2113 return VERR_IOM_IOPORT_UNUSED;
2114 }
2115}
2116
2117static DECLCALLBACK(int) fdcSaveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
2118{
2119 fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2120 QEMUFile *f = pSSMHandle;
2121 unsigned int i;
2122
2123 /* Save the FDC I/O registers... */
2124 SSMR3PutU8(pSSMHandle, s->sra);
2125 SSMR3PutU8(pSSMHandle, s->srb);
2126 SSMR3PutU8(pSSMHandle, s->dor);
2127 SSMR3PutU8(pSSMHandle, s->tdr);
2128 SSMR3PutU8(pSSMHandle, s->dsr);
2129 SSMR3PutU8(pSSMHandle, s->msr);
2130 /* ...the status registers... */
2131 SSMR3PutU8(pSSMHandle, s->status0);
2132 SSMR3PutU8(pSSMHandle, s->status1);
2133 SSMR3PutU8(pSSMHandle, s->status2);
2134 /* ...the command FIFO... */
2135 SSMR3PutU32(pSSMHandle, sizeof(s->fifo));
2136 SSMR3PutMem(pSSMHandle, &s->fifo, sizeof(s->fifo));
2137 SSMR3PutU32(pSSMHandle, s->data_pos);
2138 SSMR3PutU32(pSSMHandle, s->data_len);
2139 SSMR3PutU8(pSSMHandle, s->data_state);
2140 SSMR3PutU8(pSSMHandle, s->data_dir);
2141 /* ...and miscellaneous internal FDC state. */
2142 SSMR3PutU8(pSSMHandle, s->reset_sensei);
2143 SSMR3PutU8(pSSMHandle, s->eot);
2144 SSMR3PutU8(pSSMHandle, s->timer0);
2145 SSMR3PutU8(pSSMHandle, s->timer1);
2146 SSMR3PutU8(pSSMHandle, s->precomp_trk);
2147 SSMR3PutU8(pSSMHandle, s->config);
2148 SSMR3PutU8(pSSMHandle, s->lock);
2149 SSMR3PutU8(pSSMHandle, s->pwrd);
2150 SSMR3PutU8(pSSMHandle, s->version);
2151
2152 /* Save the number of drives and per-drive state. Note that the media
2153 * states will be updated in fd_revalidate() and need not be saved.
2154 */
2155 SSMR3PutU8(pSSMHandle, s->num_floppies);
2156 Assert(RT_ELEMENTS(s->drives) == s->num_floppies);
2157 for (i = 0; i < s->num_floppies; ++i) {
2158 fdrive_t *d = &s->drives[i];
2159
2160 SSMR3PutMem(pSSMHandle, &d->Led, sizeof(d->Led));
2161 SSMR3PutU32(pSSMHandle, d->drive);
2162 SSMR3PutU8(pSSMHandle, d->dsk_chg);
2163 SSMR3PutU8(pSSMHandle, d->perpendicular);
2164 SSMR3PutU8(pSSMHandle, d->head);
2165 SSMR3PutU8(pSSMHandle, d->track);
2166 SSMR3PutU8(pSSMHandle, d->sect);
2167 }
2168 return TMR3TimerSave (s->result_timer, pSSMHandle);
2169}
2170
2171static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns,
2172 PSSMHANDLE pSSMHandle,
2173 uint32_t uVersion,
2174 uint32_t uPass)
2175{
2176 fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2177 QEMUFile *f = pSSMHandle;
2178 unsigned int i;
2179 uint32_t val32;
2180 uint8_t val8;
2181
2182 if (uVersion > FDC_SAVESTATE_CURRENT)
2183 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2184 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
2185
2186 /* The old saved state was significantly different. However, we can get
2187 * back most of the controller state and fix the rest by pretending the
2188 * disk in the drive (if any) has been replaced. At any rate there should
2189 * be no difficulty unless the state was saved during a floppy operation.
2190 */
2191 if (uVersion == FDC_SAVESTATE_OLD)
2192 {
2193 /* First verify a few assumptions. */
2194 AssertMsgReturn(sizeof(s->fifo) == FD_SECTOR_LEN,
2195 ("The size of FIFO in saved state doesn't match!\n"),
2196 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2197 AssertMsgReturn(RT_ELEMENTS(s->drives) == 2,
2198 ("The number of drives in old saved state doesn't match!\n"),
2199 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2200 /* Now load the old state. */
2201 SSMR3GetU8(pSSMHandle, &s->version);
2202 /* Toss IRQ level, DMA channel, I/O base, and state. */
2203 SSMR3GetU8(pSSMHandle, &val8);
2204 SSMR3GetU8(pSSMHandle, &val8);
2205 SSMR3GetU32(pSSMHandle, &val32);
2206 SSMR3GetU8(pSSMHandle, &val8);
2207 /* Translate dma_en. */
2208 SSMR3GetU8(pSSMHandle, &val8);
2209 if (val8)
2210 s->dor |= FD_DOR_DMAEN;
2211 SSMR3GetU8(pSSMHandle, &s->cur_drv);
2212 /* Translate bootsel. */
2213 SSMR3GetU8(pSSMHandle, &val8);
2214 s->tdr |= val8 << 2;
2215 SSMR3GetMem(pSSMHandle, &s->fifo, FD_SECTOR_LEN);
2216 SSMR3GetU32(pSSMHandle, &s->data_pos);
2217 SSMR3GetU32(pSSMHandle, &s->data_len);
2218 SSMR3GetU8(pSSMHandle, &s->data_state);
2219 SSMR3GetU8(pSSMHandle, &s->data_dir);
2220 SSMR3GetU8(pSSMHandle, &s->status0);
2221 SSMR3GetU8(pSSMHandle, &s->eot);
2222 SSMR3GetU8(pSSMHandle, &s->timer0);
2223 SSMR3GetU8(pSSMHandle, &s->timer1);
2224 SSMR3GetU8(pSSMHandle, &s->precomp_trk);
2225 SSMR3GetU8(pSSMHandle, &s->config);
2226 SSMR3GetU8(pSSMHandle, &s->lock);
2227 SSMR3GetU8(pSSMHandle, &s->pwrd);
2228
2229 for (i = 0; i < 2; ++i) {
2230 fdrive_t *d = &s->drives[i];
2231
2232 SSMR3GetMem (pSSMHandle, &d->Led, sizeof (d->Led));
2233 SSMR3GetU32(pSSMHandle, &val32);
2234 d->drive = val32;
2235 SSMR3GetU32(pSSMHandle, &val32); /* Toss drflags */
2236 SSMR3GetU8(pSSMHandle, &d->perpendicular);
2237 SSMR3GetU8(pSSMHandle, &d->head);
2238 SSMR3GetU8(pSSMHandle, &d->track);
2239 SSMR3GetU8(pSSMHandle, &d->sect);
2240 SSMR3GetU8(pSSMHandle, &val8); /* Toss dir, rw */
2241 SSMR3GetU8(pSSMHandle, &val8);
2242 SSMR3GetU32(pSSMHandle, &val32);
2243 d->flags = val32;
2244 SSMR3GetU8(pSSMHandle, &d->last_sect);
2245 SSMR3GetU8(pSSMHandle, &d->max_track);
2246 SSMR3GetU16(pSSMHandle, &d->bps);
2247 SSMR3GetU8(pSSMHandle, &d->ro);
2248 }
2249 }
2250 else /* New state - straightforward. */
2251 {
2252 Assert(uVersion == FDC_SAVESTATE_CURRENT);
2253 /* Load the FDC I/O registers... */
2254 SSMR3GetU8(pSSMHandle, &s->sra);
2255 SSMR3GetU8(pSSMHandle, &s->srb);
2256 SSMR3GetU8(pSSMHandle, &s->dor);
2257 SSMR3GetU8(pSSMHandle, &s->tdr);
2258 SSMR3GetU8(pSSMHandle, &s->dsr);
2259 SSMR3GetU8(pSSMHandle, &s->msr);
2260 /* ...the status registers... */
2261 SSMR3GetU8(pSSMHandle, &s->status0);
2262 SSMR3GetU8(pSSMHandle, &s->status1);
2263 SSMR3GetU8(pSSMHandle, &s->status2);
2264 /* ...the command FIFO, if the size matches... */
2265 SSMR3GetU32(pSSMHandle, &val32);
2266 AssertMsgReturn(sizeof(s->fifo) == val32,
2267 ("The size of FIFO in saved state doesn't match!\n"),
2268 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2269 SSMR3GetMem(pSSMHandle, &s->fifo, sizeof(s->fifo));
2270 SSMR3GetU32(pSSMHandle, &s->data_pos);
2271 SSMR3GetU32(pSSMHandle, &s->data_len);
2272 SSMR3GetU8(pSSMHandle, &s->data_state);
2273 SSMR3GetU8(pSSMHandle, &s->data_dir);
2274 /* ...and miscellaneous internal FDC state. */
2275 SSMR3GetU8(pSSMHandle, &s->reset_sensei);
2276 SSMR3GetU8(pSSMHandle, &s->eot);
2277 SSMR3GetU8(pSSMHandle, &s->timer0);
2278 SSMR3GetU8(pSSMHandle, &s->timer1);
2279 SSMR3GetU8(pSSMHandle, &s->precomp_trk);
2280 SSMR3GetU8(pSSMHandle, &s->config);
2281 SSMR3GetU8(pSSMHandle, &s->lock);
2282 SSMR3GetU8(pSSMHandle, &s->pwrd);
2283 SSMR3GetU8(pSSMHandle, &s->version);
2284
2285 /* Validate the number of drives. */
2286 SSMR3GetU8(pSSMHandle, &s->num_floppies);
2287 AssertMsgReturn(RT_ELEMENTS(s->drives) == s->num_floppies,
2288 ("The number of drives in saved state doesn't match!\n"),
2289 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2290
2291 /* Load the per-drive state. */
2292 for (i = 0; i < s->num_floppies; ++i) {
2293 fdrive_t *d = &s->drives[i];
2294
2295 SSMR3GetMem(pSSMHandle, &d->Led, sizeof(d->Led));
2296 SSMR3GetU32(pSSMHandle, &val32);
2297 d->drive = val32;
2298 SSMR3GetU8(pSSMHandle, &d->dsk_chg);
2299 SSMR3GetU8(pSSMHandle, &d->perpendicular);
2300 SSMR3GetU8(pSSMHandle, &d->head);
2301 SSMR3GetU8(pSSMHandle, &d->track);
2302 SSMR3GetU8(pSSMHandle, &d->sect);
2303 }
2304 }
2305 return TMR3TimerLoad (s->result_timer, pSSMHandle);
2306}
2307
2308/**
2309 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2310 */
2311static DECLCALLBACK(void *) fdQueryInterface (PPDMIBASE pInterface, const char *pszIID)
2312{
2313 fdrive_t *pDrive = PDMIBASE_2_FDRIVE(pInterface);
2314
2315 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrive->IBase);
2316 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrive->IPort);
2317 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrive->IMountNotify);
2318 return NULL;
2319}
2320
2321/**
2322 * Gets the pointer to the status LED of a unit.
2323 *
2324 * @returns VBox status code.
2325 * @param pInterface Pointer to the interface structure containing the called function pointer.
2326 * @param iLUN The unit which status LED we desire.
2327 * @param ppLed Where to store the LED pointer.
2328 */
2329static DECLCALLBACK(int) fdcStatusQueryStatusLed (PPDMILEDPORTS pInterface,
2330 unsigned iLUN,
2331 PPDMLED *ppLed)
2332{
2333 fdctrl_t *fdctrl = (fdctrl_t *)
2334 ((uintptr_t )pInterface - RT_OFFSETOF (fdctrl_t, ILeds));
2335 if (iLUN < RT_ELEMENTS(fdctrl->drives)) {
2336 *ppLed = &fdctrl->drives[iLUN].Led;
2337 Assert ((*ppLed)->u32Magic == PDMLED_MAGIC);
2338 return VINF_SUCCESS;
2339 }
2340 return VERR_PDM_LUN_NOT_FOUND;
2341}
2342
2343
2344/**
2345 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2346 */
2347static DECLCALLBACK(void *) fdcStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2348{
2349 fdctrl_t *pThis = RT_FROM_MEMBER (pInterface, fdctrl_t, IBaseStatus);
2350
2351 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBaseStatus);
2352 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
2353 return NULL;
2354}
2355
2356
2357/**
2358 * Configure a drive.
2359 *
2360 * @returns VBox status code.
2361 * @param drv The drive in question.
2362 * @param pDevIns The driver instance.
2363 */
2364static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
2365{
2366 static const char *descs[] = {"Floppy Drive A:", "Floppy Drive B"};
2367 int rc;
2368
2369 /*
2370 * Reset the LED just to be on the safe side.
2371 */
2372 Assert (RT_ELEMENTS(descs) > drv->iLUN);
2373 Assert (drv->Led.u32Magic == PDMLED_MAGIC);
2374 drv->Led.Actual.u32 = 0;
2375 drv->Led.Asserted.u32 = 0;
2376
2377 /*
2378 * Try attach the block device and get the interfaces.
2379 */
2380 rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, descs[drv->iLUN]);
2381 if (RT_SUCCESS (rc)) {
2382 drv->pDrvBlock = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCK);
2383 if (drv->pDrvBlock) {
2384 drv->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCKBIOS);
2385 if (drv->pDrvBlockBios) {
2386 drv->pDrvMount = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIMOUNT);
2387 if (drv->pDrvMount) {
2388 fd_init(drv);
2389 } else {
2390 AssertMsgFailed (("Configuration error: LUN#%d without mountable interface!\n", drv->iLUN));
2391 rc = VERR_PDM_MISSING_INTERFACE;
2392 }
2393
2394 } else {
2395 AssertMsgFailed (("Configuration error: LUN#%d hasn't a block BIOS interface!\n", drv->iLUN));
2396 rc = VERR_PDM_MISSING_INTERFACE;
2397 }
2398
2399 } else {
2400 AssertMsgFailed (("Configuration error: LUN#%d hasn't a block interface!\n", drv->iLUN));
2401 rc = VERR_PDM_MISSING_INTERFACE;
2402 }
2403 } else {
2404 AssertMsg (rc == VERR_PDM_NO_ATTACHED_DRIVER,
2405 ("Failed to attach LUN#%d. rc=%Rrc\n", drv->iLUN, rc));
2406 switch (rc) {
2407 case VERR_ACCESS_DENIED:
2408 /* Error already cached by DrvHostBase */
2409 break;
2410 case VERR_PDM_NO_ATTACHED_DRIVER:
2411 /* Legal on architectures without a floppy controller */
2412 break;
2413 default:
2414 rc = PDMDevHlpVMSetError (pDevIns, rc, RT_SRC_POS,
2415 N_ ("The floppy controller cannot attach to the floppy drive"));
2416 break;
2417 }
2418 }
2419
2420 if (RT_FAILURE (rc)) {
2421 drv->pDrvBase = NULL;
2422 drv->pDrvBlock = NULL;
2423 drv->pDrvBlockBios = NULL;
2424 drv->pDrvMount = NULL;
2425 }
2426 LogFlow (("fdConfig: returns %Rrc\n", rc));
2427 return rc;
2428}
2429
2430
2431/**
2432 * Attach command.
2433 *
2434 * This is called when we change block driver for a floppy drive.
2435 *
2436 * @returns VBox status code.
2437 * @param pDevIns The device instance.
2438 * @param iLUN The logical unit which is being detached.
2439 */
2440static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns,
2441 unsigned iLUN, uint32_t fFlags)
2442{
2443 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2444 fdrive_t *drv;
2445 int rc;
2446 LogFlow (("ideDetach: iLUN=%u\n", iLUN));
2447
2448 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2449 ("The FDC device does not support hotplugging\n"),
2450 VERR_INVALID_PARAMETER);
2451
2452 /*
2453 * Validate.
2454 */
2455 if (iLUN > 2) {
2456 AssertMsgFailed (("Configuration error: cannot attach or detach any but the first two LUNs - iLUN=%u\n",
2457 iLUN));
2458 return VERR_PDM_DEVINS_NO_ATTACH;
2459 }
2460
2461 /*
2462 * Locate the drive and stuff.
2463 */
2464 drv = &fdctrl->drives[iLUN];
2465
2466 /* the usual paranoia */
2467 AssertRelease (!drv->pDrvBase);
2468 AssertRelease (!drv->pDrvBlock);
2469 AssertRelease (!drv->pDrvBlockBios);
2470 AssertRelease (!drv->pDrvMount);
2471
2472 rc = fdConfig (drv, pDevIns);
2473 AssertMsg (rc != VERR_PDM_NO_ATTACHED_DRIVER,
2474 ("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
2475 if (RT_SUCCESS(rc)) {
2476 fd_revalidate (drv);
2477 }
2478
2479 LogFlow (("floppyAttach: returns %Rrc\n", rc));
2480 return rc;
2481}
2482
2483
2484/**
2485 * Detach notification.
2486 *
2487 * The floppy drive has been temporarily 'unplugged'.
2488 *
2489 * @param pDevIns The device instance.
2490 * @param iLUN The logical unit which is being detached.
2491 */
2492static DECLCALLBACK(void) fdcDetach (PPDMDEVINS pDevIns,
2493 unsigned iLUN, uint32_t fFlags)
2494{
2495 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2496 LogFlow (("ideDetach: iLUN=%u\n", iLUN));
2497
2498 switch (iLUN) {
2499 case 0:
2500 case 1: {
2501 fdrive_t *drv = &fdctrl->drives[iLUN];
2502 drv->pDrvBase = NULL;
2503 drv->pDrvBlock = NULL;
2504 drv->pDrvBlockBios = NULL;
2505 drv->pDrvMount = NULL;
2506 break;
2507 }
2508
2509 default:
2510 AssertMsgFailed (("Cannot detach LUN#%d!\n", iLUN));
2511 break;
2512 }
2513}
2514
2515
2516/**
2517 * Handle reset.
2518 *
2519 * I haven't check the specs on what's supposed to happen on reset, but we
2520 * should get any 'FATAL: floppy recal:f07 ctrl not ready' when resetting
2521 * at wrong time like we do if this was all void.
2522 *
2523 * @param pDevIns The device instance.
2524 */
2525static DECLCALLBACK(void) fdcReset (PPDMDEVINS pDevIns)
2526{
2527 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2528 unsigned i;
2529 LogFlow (("fdcReset:\n"));
2530
2531 fdctrl_reset(fdctrl, 0);
2532
2533 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
2534 fd_revalidate(&fdctrl->drives[i]);
2535 }
2536}
2537
2538
2539/**
2540 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2541 */
2542static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns,
2543 int iInstance,
2544 PCFGMNODE pCfg)
2545{
2546 int rc;
2547 fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t*);
2548 unsigned i, j;
2549 int ii;
2550 bool mem_mapped;
2551 uint16_t io_base;
2552 uint8_t irq_lvl, dma_chann;
2553 PPDMIBASE pBase;
2554
2555 Assert(iInstance == 0);
2556 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2557
2558 /*
2559 * Validate configuration.
2560 */
2561 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0DMA\0MemMapped\0IOBase\0"))
2562 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
2563
2564 /*
2565 * Read the configuration.
2566 */
2567 rc = CFGMR3QueryU8 (pCfg, "IRQ", &irq_lvl);
2568 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2569 irq_lvl = 6;
2570 else if (RT_FAILURE (rc)) {
2571 AssertMsgFailed (("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc));
2572 return rc;
2573 }
2574
2575 rc = CFGMR3QueryU8 (pCfg, "DMA", &dma_chann);
2576 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2577 dma_chann = 2;
2578 else if (RT_FAILURE (rc)) {
2579 AssertMsgFailed (("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc));
2580 return rc;
2581 }
2582
2583 rc = CFGMR3QueryU16 (pCfg, "IOBase", &io_base);
2584 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2585 io_base = 0x3f0;
2586 else if (RT_FAILURE (rc)) {
2587 AssertMsgFailed (("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc));
2588 return rc;
2589 }
2590
2591 rc = CFGMR3QueryBool (pCfg, "MemMapped", &mem_mapped);
2592 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2593 mem_mapped = false;
2594 else if (RT_FAILURE (rc)) {
2595 AssertMsgFailed (("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc));
2596 return rc;
2597 }
2598
2599 /*
2600 * Initialize data.
2601 */
2602 LogFlow(("fdcConstruct: irq_lvl=%d dma_chann=%d io_base=%#x\n", irq_lvl, dma_chann, io_base));
2603 fdctrl->pDevIns = pDevIns;
2604 fdctrl->version = 0x90; /* Intel 82078 controller */
2605 fdctrl->irq_lvl = irq_lvl;
2606 fdctrl->dma_chann = dma_chann;
2607 fdctrl->io_base = io_base;
2608 fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
2609 fdctrl->num_floppies = MAX_FD;
2610
2611 /* Fill 'command_to_handler' lookup table */
2612 for (ii = RT_ELEMENTS(handlers) - 1; ii >= 0; ii--) {
2613 for (j = 0; j < sizeof(command_to_handler); j++) {
2614 if ((j & handlers[ii].mask) == handlers[ii].value) {
2615 command_to_handler[j] = ii;
2616 }
2617 }
2618 }
2619
2620 fdctrl->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface;
2621 fdctrl->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed;
2622
2623 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); ++i) {
2624 fdrive_t *drv = &fdctrl->drives[i];
2625
2626 drv->drive = FDRIVE_DRV_NONE;
2627 drv->iLUN = i;
2628
2629 drv->IBase.pfnQueryInterface = fdQueryInterface;
2630 drv->IMountNotify.pfnMountNotify = fdMountNotify;
2631 drv->IMountNotify.pfnUnmountNotify = fdUnmountNotify;
2632 drv->Led.u32Magic = PDMLED_MAGIC;
2633 }
2634
2635 /*
2636 * Create the FDC timer.
2637 */
2638 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdc_timer, fdctrl,
2639 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &fdctrl->result_timer);
2640 if (RT_FAILURE (rc))
2641 return rc;
2642
2643 /*
2644 * Register DMA channel.
2645 */
2646 if (fdctrl->dma_chann != 0xff) {
2647 rc = PDMDevHlpDMARegister (pDevIns, dma_chann, &fdctrl_transfer_handler, fdctrl);
2648 if (RT_FAILURE (rc))
2649 return rc;
2650 }
2651
2652 /*
2653 * IO / MMIO.
2654 */
2655 if (mem_mapped) {
2656 AssertMsgFailed (("Memory mapped floppy not support by now\n"));
2657 return VERR_NOT_SUPPORTED;
2658#if 0
2659 FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
2660 io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
2661 cpu_register_physical_memory(base, 0x08, io_mem);
2662#endif
2663 } else {
2664 rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x1, 5, fdctrl,
2665 fdc_io_write, fdc_io_read, NULL, NULL, "FDC#1");
2666 if (RT_FAILURE (rc))
2667 return rc;
2668
2669 rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x7, 1, fdctrl,
2670 fdc_io_write, fdc_io_read, NULL, NULL, "FDC#2");
2671 if (RT_FAILURE (rc))
2672 return rc;
2673 }
2674
2675 /*
2676 * Register the saved state data unit.
2677 */
2678 rc = PDMDevHlpSSMRegister (pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*fdctrl), fdcSaveExec, fdcLoadExec);
2679 if (RT_FAILURE(rc))
2680 return rc;
2681
2682 /*
2683 * Attach the status port (optional).
2684 */
2685 rc = PDMDevHlpDriverAttach (pDevIns, PDM_STATUS_LUN, &fdctrl->IBaseStatus, &pBase, "Status Port");
2686 if (RT_SUCCESS (rc)) {
2687 fdctrl->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
2688 } else if (rc != VERR_PDM_NO_ATTACHED_DRIVER) {
2689 AssertMsgFailed (("Failed to attach to status driver. rc=%Rrc\n",
2690 rc));
2691 return rc;
2692 }
2693
2694 /*
2695 * Initialize drives.
2696 */
2697 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
2698 fdrive_t *drv = &fdctrl->drives[i];
2699 rc = fdConfig (drv, pDevIns);
2700 if ( RT_FAILURE (rc)
2701 && rc != VERR_PDM_NO_ATTACHED_DRIVER) {
2702 AssertMsgFailed (("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
2703 return rc;
2704 }
2705 }
2706
2707 fdctrl_reset(fdctrl, 0);
2708
2709 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++)
2710 fd_revalidate(&fdctrl->drives[i]);
2711
2712 return VINF_SUCCESS;
2713}
2714
2715/**
2716 * The device registration structure.
2717 */
2718const PDMDEVREG g_DeviceFloppyController =
2719{
2720 /* u32Version */
2721 PDM_DEVREG_VERSION,
2722 /* szName */
2723 "i82078",
2724 /* szRCMod */
2725 "",
2726 /* szR0Mod */
2727 "",
2728 /* pszDescription */
2729 "Floppy drive controller (Intel 82078)",
2730 /* fFlags */
2731 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2732 /* fClass */
2733 PDM_DEVREG_CLASS_STORAGE,
2734 /* cMaxInstances */
2735 1,
2736 /* cbInstance */
2737 sizeof(fdctrl_t),
2738 /* pfnConstruct */
2739 fdcConstruct,
2740 /* pfnDestruct */
2741 NULL,
2742 /* pfnRelocate */
2743 NULL,
2744 /* pfnIOCtl */
2745 NULL,
2746 /* pfnPowerOn */
2747 NULL,
2748 /* pfnReset */
2749 fdcReset,
2750 /* pfnSuspend */
2751 NULL,
2752 /* pfnResume */
2753 NULL,
2754 /* pfnAttach */
2755 fdcAttach,
2756 /* pfnDetach */
2757 fdcDetach,
2758 /* pfnQueryInterface. */
2759 NULL,
2760 /* pfnInitComplete */
2761 NULL,
2762 /* pfnPowerOff */
2763 NULL,
2764 /* pfnSoftReset */
2765 NULL,
2766 /* u32VersionEnd */
2767 PDM_DEVREG_VERSION
2768};
2769
2770#endif /* VBOX */
2771
2772/*
2773 * Local Variables:
2774 * mode: c
2775 * c-file-style: "k&r"
2776 * indent-tabs-mode: nil
2777 * End:
2778 */
2779
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette