VirtualBox

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

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

header (C) fixes

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