VirtualBox

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

Last change on this file since 47645 was 47611, checked in by vboxsync, 11 years ago

DevFDC: Explicitly added a 1.2M 3.5in floppy type.

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