VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DevFdc.cpp@ 44807

Last change on this file since 44807 was 44807, checked in by vboxsync, 12 years ago

fdc.c -> DevFdc.cpp.

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