VirtualBox

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

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

BIOS,DevFdc,DrvBlock: The floppy controller should query the block/host driver for the drive type, or it'll all go real bad if the guest code trusts CMOS/BIOS drive info. Changed the block driver to automatically upgrade the drive type (for fdc and bios/cmos setup only) if the image is larger than the configured drive capacity. Introduced two fake drive types with max capacities, 15.6 MB and 63.5 MB, the first is the max that INT13 can officially access, the second is reinterpreting CL as holding an 8-bit wide sector number and no cylinder bits (which actually seems to be how real bioses work with floppies).

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