ia64/xen-unstable

view tools/ioemu/hw/fdc.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 8e5fc5fe636c
children f7b43e5c42b9
line source
1 /*
2 * QEMU Floppy disk emulator (Intel 82078)
3 *
4 * Copyright (c) 2003 Jocelyn Mayer
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include "vl.h"
26 /********************************************************/
27 /* debug Floppy devices */
28 //#define DEBUG_FLOPPY
30 #ifdef DEBUG_FLOPPY
31 #define FLOPPY_DPRINTF(fmt, args...) \
32 do { printf("FLOPPY: " fmt , ##args); } while (0)
33 #else
34 #define FLOPPY_DPRINTF(fmt, args...)
35 #endif
37 #define FLOPPY_ERROR(fmt, args...) \
38 do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)
40 /********************************************************/
41 /* Floppy drive emulation */
43 /* Will always be a fixed parameter for us */
44 #define FD_SECTOR_LEN 512
45 #define FD_SECTOR_SC 2 /* Sector size code */
47 /* Floppy disk drive emulation */
48 typedef enum fdisk_type_t {
49 FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */
50 FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */
51 FDRIVE_DISK_720 = 0x03, /* 720 kB disk */
52 FDRIVE_DISK_USER = 0x04, /* User defined geometry */
53 FDRIVE_DISK_NONE = 0x05, /* No disk */
54 } fdisk_type_t;
56 typedef enum fdrive_type_t {
57 FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
58 FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
59 FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
60 FDRIVE_DRV_NONE = 0x03, /* No drive connected */
61 } fdrive_type_t;
63 typedef enum fdrive_flags_t {
64 FDRIVE_MOTOR_ON = 0x01, /* motor on/off */
65 FDRIVE_REVALIDATE = 0x02, /* Revalidated */
66 } fdrive_flags_t;
68 typedef enum fdisk_flags_t {
69 FDISK_DBL_SIDES = 0x01,
70 } fdisk_flags_t;
72 typedef struct fdrive_t {
73 BlockDriverState *bs;
74 /* Drive status */
75 fdrive_type_t drive;
76 fdrive_flags_t drflags;
77 uint8_t perpendicular; /* 2.88 MB access mode */
78 /* Position */
79 uint8_t head;
80 uint8_t track;
81 uint8_t sect;
82 /* Last operation status */
83 uint8_t dir; /* Direction */
84 uint8_t rw; /* Read/write */
85 /* Media */
86 fdisk_flags_t flags;
87 uint8_t last_sect; /* Nb sector per track */
88 uint8_t max_track; /* Nb of tracks */
89 uint16_t bps; /* Bytes per sector */
90 uint8_t ro; /* Is read-only */
91 } fdrive_t;
93 static void fd_init (fdrive_t *drv, BlockDriverState *bs)
94 {
95 /* Drive */
96 drv->bs = bs;
97 drv->drive = FDRIVE_DRV_NONE;
98 drv->drflags = 0;
99 drv->perpendicular = 0;
100 /* Disk */
101 drv->last_sect = 0;
102 drv->max_track = 0;
103 }
105 static int _fd_sector (uint8_t head, uint8_t track,
106 uint8_t sect, uint8_t last_sect)
107 {
108 return (((track * 2) + head) * last_sect) + sect - 1;
109 }
111 /* Returns current position, in sectors, for given drive */
112 static int fd_sector (fdrive_t *drv)
113 {
114 return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);
115 }
117 static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
118 int enable_seek)
119 {
120 uint32_t sector;
121 int ret;
123 if (track > drv->max_track ||
124 (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
125 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
126 head, track, sect, 1,
127 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
128 drv->max_track, drv->last_sect);
129 return 2;
130 }
131 if (sect > drv->last_sect) {
132 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
133 head, track, sect, 1,
134 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
135 drv->max_track, drv->last_sect);
136 return 3;
137 }
138 sector = _fd_sector(head, track, sect, drv->last_sect);
139 ret = 0;
140 if (sector != fd_sector(drv)) {
141 #if 0
142 if (!enable_seek) {
143 FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
144 head, track, sect, 1, drv->max_track, drv->last_sect);
145 return 4;
146 }
147 #endif
148 drv->head = head;
149 if (drv->track != track)
150 ret = 1;
151 drv->track = track;
152 drv->sect = sect;
153 }
155 return ret;
156 }
158 /* Set drive back to track 0 */
159 static void fd_recalibrate (fdrive_t *drv)
160 {
161 FLOPPY_DPRINTF("recalibrate\n");
162 drv->head = 0;
163 drv->track = 0;
164 drv->sect = 1;
165 drv->dir = 1;
166 drv->rw = 0;
167 }
169 /* Recognize floppy formats */
170 typedef struct fd_format_t {
171 fdrive_type_t drive;
172 fdisk_type_t disk;
173 uint8_t last_sect;
174 uint8_t max_track;
175 uint8_t max_head;
176 const unsigned char *str;
177 } fd_format_t;
179 static fd_format_t fd_formats[] = {
180 /* First entry is default format */
181 /* 1.44 MB 3"1/2 floppy disks */
182 { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
183 { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", },
184 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
185 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
186 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
187 { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
188 { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
189 { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
190 /* 2.88 MB 3"1/2 floppy disks */
191 { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
192 { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
193 { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", },
194 { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
195 { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
196 /* 720 kB 3"1/2 floppy disks */
197 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", },
198 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", },
199 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", },
200 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", },
201 { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
202 { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
203 /* 1.2 MB 5"1/4 floppy disks */
204 { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", },
205 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
206 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
207 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
208 { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", },
209 /* 720 kB 5"1/4 floppy disks */
210 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", },
211 { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", },
212 /* 360 kB 5"1/4 floppy disks */
213 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", },
214 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", },
215 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", },
216 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", },
217 /* 320 kB 5"1/4 floppy disks */
218 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", },
219 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", },
220 /* 360 kB must match 5"1/4 better than 3"1/2... */
221 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", },
222 /* end */
223 { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
224 };
226 /* Revalidate a disk drive after a disk change */
227 static void fd_revalidate (fdrive_t *drv)
228 {
229 fd_format_t *parse;
230 int64_t nb_sectors, size;
231 int i, first_match, match;
232 int nb_heads, max_track, last_sect, ro;
234 FLOPPY_DPRINTF("revalidate\n");
235 drv->drflags &= ~FDRIVE_REVALIDATE;
236 if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
237 ro = bdrv_is_read_only(drv->bs);
238 bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
239 if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
240 FLOPPY_DPRINTF("User defined disk (%d %d %d)",
241 nb_heads - 1, max_track, last_sect);
242 } else {
243 bdrv_get_geometry(drv->bs, &nb_sectors);
244 match = -1;
245 first_match = -1;
246 for (i = 0;; i++) {
247 parse = &fd_formats[i];
248 if (parse->drive == FDRIVE_DRV_NONE)
249 break;
250 if (drv->drive == parse->drive ||
251 drv->drive == FDRIVE_DRV_NONE) {
252 size = (parse->max_head + 1) * parse->max_track *
253 parse->last_sect;
254 if (nb_sectors == size) {
255 match = i;
256 break;
257 }
258 if (first_match == -1)
259 first_match = i;
260 }
261 }
262 if (match == -1) {
263 if (first_match == -1)
264 match = 1;
265 else
266 match = first_match;
267 parse = &fd_formats[match];
268 }
269 nb_heads = parse->max_head + 1;
270 max_track = parse->max_track;
271 last_sect = parse->last_sect;
272 drv->drive = parse->drive;
273 FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
274 nb_heads, max_track, last_sect, ro ? "ro" : "rw");
275 }
276 if (nb_heads == 1) {
277 drv->flags &= ~FDISK_DBL_SIDES;
278 } else {
279 drv->flags |= FDISK_DBL_SIDES;
280 }
281 drv->max_track = max_track;
282 drv->last_sect = last_sect;
283 drv->ro = ro;
284 } else {
285 FLOPPY_DPRINTF("No disk in drive\n");
286 drv->last_sect = 0;
287 drv->max_track = 0;
288 drv->flags &= ~FDISK_DBL_SIDES;
289 }
290 drv->drflags |= FDRIVE_REVALIDATE;
291 }
293 /* Motor control */
294 static void fd_start (fdrive_t *drv)
295 {
296 drv->drflags |= FDRIVE_MOTOR_ON;
297 }
299 static void fd_stop (fdrive_t *drv)
300 {
301 drv->drflags &= ~FDRIVE_MOTOR_ON;
302 }
304 /* Re-initialise a drives (motor off, repositioned) */
305 static void fd_reset (fdrive_t *drv)
306 {
307 fd_stop(drv);
308 fd_recalibrate(drv);
309 }
311 /********************************************************/
312 /* Intel 82078 floppy disk controller emulation */
314 static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
315 static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
316 static int fdctrl_transfer_handler (void *opaque, int nchan,
317 int dma_pos, int dma_len);
318 static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
319 static void fdctrl_result_timer(void *opaque);
321 static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl);
322 static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl);
323 static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value);
324 static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl);
325 static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value);
326 static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl);
327 static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value);
328 static uint32_t fdctrl_read_data (fdctrl_t *fdctrl);
329 static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value);
330 static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl);
332 enum {
333 FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */
334 FD_CTRL_RESET = 0x02,
335 FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */
336 FD_CTRL_BUSY = 0x08, /* dma transfer in progress */
337 FD_CTRL_INTR = 0x10,
338 };
340 enum {
341 FD_DIR_WRITE = 0,
342 FD_DIR_READ = 1,
343 FD_DIR_SCANE = 2,
344 FD_DIR_SCANL = 3,
345 FD_DIR_SCANH = 4,
346 };
348 enum {
349 FD_STATE_CMD = 0x00,
350 FD_STATE_STATUS = 0x01,
351 FD_STATE_DATA = 0x02,
352 FD_STATE_STATE = 0x03,
353 FD_STATE_MULTI = 0x10,
354 FD_STATE_SEEK = 0x20,
355 FD_STATE_FORMAT = 0x40,
356 };
358 #define FD_STATE(state) ((state) & FD_STATE_STATE)
359 #define FD_SET_STATE(state, new_state) \
360 do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)
361 #define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
362 #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
363 #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
365 struct fdctrl_t {
366 fdctrl_t *fdctrl;
367 /* Controller's identification */
368 uint8_t version;
369 /* HW */
370 int irq_lvl;
371 int dma_chann;
372 uint32_t io_base;
373 /* Controller state */
374 QEMUTimer *result_timer;
375 uint8_t state;
376 uint8_t dma_en;
377 uint8_t cur_drv;
378 uint8_t bootsel;
379 /* Command FIFO */
380 uint8_t fifo[FD_SECTOR_LEN];
381 uint32_t data_pos;
382 uint32_t data_len;
383 uint8_t data_state;
384 uint8_t data_dir;
385 uint8_t int_status;
386 uint8_t eot; /* last wanted sector */
387 /* States kept only to be returned back */
388 /* Timers state */
389 uint8_t timer0;
390 uint8_t timer1;
391 /* precompensation */
392 uint8_t precomp_trk;
393 uint8_t config;
394 uint8_t lock;
395 /* Power down config (also with status regB access mode */
396 uint8_t pwrd;
397 /* Floppy drives */
398 fdrive_t drives[2];
399 };
401 static uint32_t fdctrl_read (void *opaque, uint32_t reg)
402 {
403 fdctrl_t *fdctrl = opaque;
404 uint32_t retval;
406 switch (reg & 0x07) {
407 case 0x01:
408 retval = fdctrl_read_statusB(fdctrl);
409 break;
410 case 0x02:
411 retval = fdctrl_read_dor(fdctrl);
412 break;
413 case 0x03:
414 retval = fdctrl_read_tape(fdctrl);
415 break;
416 case 0x04:
417 retval = fdctrl_read_main_status(fdctrl);
418 break;
419 case 0x05:
420 retval = fdctrl_read_data(fdctrl);
421 break;
422 case 0x07:
423 retval = fdctrl_read_dir(fdctrl);
424 break;
425 default:
426 retval = (uint32_t)(-1);
427 break;
428 }
429 FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
431 return retval;
432 }
434 static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
435 {
436 fdctrl_t *fdctrl = opaque;
438 FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
440 switch (reg & 0x07) {
441 case 0x02:
442 fdctrl_write_dor(fdctrl, value);
443 break;
444 case 0x03:
445 fdctrl_write_tape(fdctrl, value);
446 break;
447 case 0x04:
448 fdctrl_write_rate(fdctrl, value);
449 break;
450 case 0x05:
451 fdctrl_write_data(fdctrl, value);
452 break;
453 default:
454 break;
455 }
456 }
458 static void fd_change_cb (void *opaque)
459 {
460 fdrive_t *drv = opaque;
462 FLOPPY_DPRINTF("disk change\n");
463 fd_revalidate(drv);
464 #if 0
465 fd_recalibrate(drv);
466 fdctrl_reset_fifo(drv->fdctrl);
467 fdctrl_raise_irq(drv->fdctrl, 0x20);
468 #endif
469 }
471 fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
472 uint32_t io_base,
473 BlockDriverState **fds)
474 {
475 fdctrl_t *fdctrl;
476 // int io_mem;
477 int i;
479 FLOPPY_DPRINTF("init controller\n");
480 fdctrl = qemu_mallocz(sizeof(fdctrl_t));
481 if (!fdctrl)
482 return NULL;
483 fdctrl->result_timer = qemu_new_timer(vm_clock,
484 fdctrl_result_timer, fdctrl);
486 fdctrl->version = 0x90; /* Intel 82078 controller */
487 fdctrl->irq_lvl = irq_lvl;
488 fdctrl->dma_chann = dma_chann;
489 fdctrl->io_base = io_base;
490 fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
491 if (fdctrl->dma_chann != -1) {
492 fdctrl->dma_en = 1;
493 DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
494 } else {
495 fdctrl->dma_en = 0;
496 }
497 for (i = 0; i < 2; i++) {
498 fd_init(&fdctrl->drives[i], fds[i]);
499 if (fds[i]) {
500 bdrv_set_change_cb(fds[i],
501 &fd_change_cb, &fdctrl->drives[i]);
502 }
503 }
504 fdctrl_reset(fdctrl, 0);
505 fdctrl->state = FD_CTRL_ACTIVE;
506 if (mem_mapped) {
507 FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
508 #if 0
509 io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
510 cpu_register_physical_memory(base, 0x08, io_mem);
511 #endif
512 } else {
513 register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl);
514 register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl);
515 register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl);
516 register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl);
517 }
518 for (i = 0; i < 2; i++) {
519 fd_revalidate(&fdctrl->drives[i]);
520 }
522 return fdctrl;
523 }
525 /* XXX: may change if moved to bdrv */
526 int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
527 {
528 return fdctrl->drives[drive_num].drive;
529 }
531 /* Change IRQ state */
532 static void fdctrl_reset_irq (fdctrl_t *fdctrl)
533 {
534 FLOPPY_DPRINTF("Reset interrupt\n");
535 pic_set_irq(fdctrl->irq_lvl, 0);
536 fdctrl->state &= ~FD_CTRL_INTR;
537 }
539 static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status)
540 {
541 if (~(fdctrl->state & FD_CTRL_INTR)) {
542 pic_set_irq(fdctrl->irq_lvl, 1);
543 fdctrl->state |= FD_CTRL_INTR;
544 }
545 FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
546 fdctrl->int_status = status;
547 }
549 /* Reset controller */
550 static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
551 {
552 int i;
554 FLOPPY_DPRINTF("reset controller\n");
555 fdctrl_reset_irq(fdctrl);
556 /* Initialise controller */
557 fdctrl->cur_drv = 0;
558 /* FIFO state */
559 fdctrl->data_pos = 0;
560 fdctrl->data_len = 0;
561 fdctrl->data_state = FD_STATE_CMD;
562 fdctrl->data_dir = FD_DIR_WRITE;
563 for (i = 0; i < MAX_FD; i++)
564 fd_reset(&fdctrl->drives[i]);
565 fdctrl_reset_fifo(fdctrl);
566 if (do_irq)
567 fdctrl_raise_irq(fdctrl, 0xc0);
568 }
570 static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
571 {
572 return &fdctrl->drives[fdctrl->bootsel];
573 }
575 static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
576 {
577 return &fdctrl->drives[1 - fdctrl->bootsel];
578 }
580 static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
581 {
582 return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl);
583 }
585 /* Status B register : 0x01 (read-only) */
586 static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
587 {
588 FLOPPY_DPRINTF("status register: 0x00\n");
589 return 0;
590 }
592 /* Digital output register : 0x02 */
593 static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
594 {
595 uint32_t retval = 0;
597 /* Drive motors state indicators */
598 if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
599 retval |= 1 << 5;
600 if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
601 retval |= 1 << 4;
602 /* DMA enable */
603 retval |= fdctrl->dma_en << 3;
604 /* Reset indicator */
605 retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
606 /* Selected drive */
607 retval |= fdctrl->cur_drv;
608 FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
610 return retval;
611 }
613 static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
614 {
615 /* Reset mode */
616 if (fdctrl->state & FD_CTRL_RESET) {
617 if (!(value & 0x04)) {
618 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
619 return;
620 }
621 }
622 FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
623 /* Drive motors state indicators */
624 if (value & 0x20)
625 fd_start(drv1(fdctrl));
626 else
627 fd_stop(drv1(fdctrl));
628 if (value & 0x10)
629 fd_start(drv0(fdctrl));
630 else
631 fd_stop(drv0(fdctrl));
632 /* DMA enable */
633 #if 0
634 if (fdctrl->dma_chann != -1)
635 fdctrl->dma_en = 1 - ((value >> 3) & 1);
636 #endif
637 /* Reset */
638 if (!(value & 0x04)) {
639 if (!(fdctrl->state & FD_CTRL_RESET)) {
640 FLOPPY_DPRINTF("controller enter RESET state\n");
641 fdctrl->state |= FD_CTRL_RESET;
642 }
643 } else {
644 if (fdctrl->state & FD_CTRL_RESET) {
645 FLOPPY_DPRINTF("controller out of RESET state\n");
646 fdctrl_reset(fdctrl, 1);
647 fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
648 }
649 }
650 /* Selected drive */
651 fdctrl->cur_drv = value & 1;
652 }
654 /* Tape drive register : 0x03 */
655 static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
656 {
657 uint32_t retval = 0;
659 /* Disk boot selection indicator */
660 retval |= fdctrl->bootsel << 2;
661 /* Tape indicators: never allowed */
662 FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
664 return retval;
665 }
667 static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
668 {
669 /* Reset mode */
670 if (fdctrl->state & FD_CTRL_RESET) {
671 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
672 return;
673 }
674 FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
675 /* Disk boot selection indicator */
676 fdctrl->bootsel = (value >> 2) & 1;
677 /* Tape indicators: never allow */
678 }
680 /* Main status register : 0x04 (read) */
681 static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
682 {
683 uint32_t retval = 0;
685 fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
686 if (!(fdctrl->state & FD_CTRL_BUSY)) {
687 /* Data transfer allowed */
688 retval |= 0x80;
689 /* Data transfer direction indicator */
690 if (fdctrl->data_dir == FD_DIR_READ)
691 retval |= 0x40;
692 }
693 /* Should handle 0x20 for SPECIFY command */
694 /* Command busy indicator */
695 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
696 FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
697 retval |= 0x10;
698 FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
700 return retval;
701 }
703 /* Data select rate register : 0x04 (write) */
704 static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
705 {
706 /* Reset mode */
707 if (fdctrl->state & FD_CTRL_RESET) {
708 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
709 return;
710 }
711 FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
712 /* Reset: autoclear */
713 if (value & 0x80) {
714 fdctrl->state |= FD_CTRL_RESET;
715 fdctrl_reset(fdctrl, 1);
716 fdctrl->state &= ~FD_CTRL_RESET;
717 }
718 if (value & 0x40) {
719 fdctrl->state |= FD_CTRL_SLEEP;
720 fdctrl_reset(fdctrl, 1);
721 }
722 // fdctrl.precomp = (value >> 2) & 0x07;
723 }
725 /* Digital input register : 0x07 (read-only) */
726 static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
727 {
728 uint32_t retval = 0;
730 if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
731 drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
732 retval |= 0x80;
733 if (retval != 0)
734 FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
735 drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
736 drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
738 return retval;
739 }
741 /* FIFO state control */
742 static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
743 {
744 fdctrl->data_dir = FD_DIR_WRITE;
745 fdctrl->data_pos = 0;
746 FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD);
747 }
749 /* Set FIFO status for the host to read */
750 static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
751 {
752 fdctrl->data_dir = FD_DIR_READ;
753 fdctrl->data_len = fifo_len;
754 fdctrl->data_pos = 0;
755 FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS);
756 if (do_irq)
757 fdctrl_raise_irq(fdctrl, 0x00);
758 }
760 /* Set an error: unimplemented/unknown command */
761 static void fdctrl_unimplemented (fdctrl_t *fdctrl)
762 {
763 #if 0
764 fdrive_t *cur_drv;
766 cur_drv = get_cur_drv(fdctrl);
767 fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv;
768 fdctrl->fifo[1] = 0x00;
769 fdctrl->fifo[2] = 0x00;
770 fdctrl_set_fifo(fdctrl, 3, 1);
771 #else
772 // fdctrl_reset_fifo(fdctrl);
773 fdctrl->fifo[0] = 0x80;
774 fdctrl_set_fifo(fdctrl, 1, 0);
775 #endif
776 }
778 /* Callback for transfer end (stop or abort) */
779 static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
780 uint8_t status1, uint8_t status2)
781 {
782 fdrive_t *cur_drv;
784 cur_drv = get_cur_drv(fdctrl);
785 FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
786 status0, status1, status2,
787 status0 | (cur_drv->head << 2) | fdctrl->cur_drv);
788 fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv;
789 fdctrl->fifo[1] = status1;
790 fdctrl->fifo[2] = status2;
791 fdctrl->fifo[3] = cur_drv->track;
792 fdctrl->fifo[4] = cur_drv->head;
793 fdctrl->fifo[5] = cur_drv->sect;
794 fdctrl->fifo[6] = FD_SECTOR_SC;
795 fdctrl->data_dir = FD_DIR_READ;
796 if (fdctrl->state & FD_CTRL_BUSY) {
797 DMA_release_DREQ(fdctrl->dma_chann);
798 fdctrl->state &= ~FD_CTRL_BUSY;
799 }
800 fdctrl_set_fifo(fdctrl, 7, 1);
801 }
803 /* Prepare a data transfer (either DMA or FIFO) */
804 static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
805 {
806 fdrive_t *cur_drv;
807 uint8_t kh, kt, ks;
808 int did_seek;
810 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
811 cur_drv = get_cur_drv(fdctrl);
812 kt = fdctrl->fifo[2];
813 kh = fdctrl->fifo[3];
814 ks = fdctrl->fifo[4];
815 FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
816 fdctrl->cur_drv, kh, kt, ks,
817 _fd_sector(kh, kt, ks, cur_drv->last_sect));
818 did_seek = 0;
819 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
820 case 2:
821 /* sect too big */
822 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
823 fdctrl->fifo[3] = kt;
824 fdctrl->fifo[4] = kh;
825 fdctrl->fifo[5] = ks;
826 return;
827 case 3:
828 /* track too big */
829 fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
830 fdctrl->fifo[3] = kt;
831 fdctrl->fifo[4] = kh;
832 fdctrl->fifo[5] = ks;
833 return;
834 case 4:
835 /* No seek enabled */
836 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
837 fdctrl->fifo[3] = kt;
838 fdctrl->fifo[4] = kh;
839 fdctrl->fifo[5] = ks;
840 return;
841 case 1:
842 did_seek = 1;
843 break;
844 default:
845 break;
846 }
847 /* Set the FIFO state */
848 fdctrl->data_dir = direction;
849 fdctrl->data_pos = 0;
850 FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */
851 if (fdctrl->fifo[0] & 0x80)
852 fdctrl->data_state |= FD_STATE_MULTI;
853 else
854 fdctrl->data_state &= ~FD_STATE_MULTI;
855 if (did_seek)
856 fdctrl->data_state |= FD_STATE_SEEK;
857 else
858 fdctrl->data_state &= ~FD_STATE_SEEK;
859 if (fdctrl->fifo[5] == 00) {
860 fdctrl->data_len = fdctrl->fifo[8];
861 } else {
862 int tmp;
863 fdctrl->data_len = 128 << fdctrl->fifo[5];
864 tmp = (cur_drv->last_sect - ks + 1);
865 if (fdctrl->fifo[0] & 0x80)
866 tmp += cur_drv->last_sect;
867 fdctrl->data_len *= tmp;
868 }
869 fdctrl->eot = fdctrl->fifo[6];
870 if (fdctrl->dma_en) {
871 int dma_mode;
872 /* DMA transfer are enabled. Check if DMA channel is well programmed */
873 dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
874 dma_mode = (dma_mode >> 2) & 3;
875 FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
876 dma_mode, direction,
877 (128 << fdctrl->fifo[5]) *
878 (cur_drv->last_sect - ks + 1), fdctrl->data_len);
879 if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
880 direction == FD_DIR_SCANH) && dma_mode == 0) ||
881 (direction == FD_DIR_WRITE && dma_mode == 2) ||
882 (direction == FD_DIR_READ && dma_mode == 1)) {
883 /* No access is allowed until DMA transfer has completed */
884 fdctrl->state |= FD_CTRL_BUSY;
885 /* Now, we just have to wait for the DMA controller to
886 * recall us...
887 */
888 DMA_hold_DREQ(fdctrl->dma_chann);
889 DMA_schedule(fdctrl->dma_chann);
890 return;
891 } else {
892 FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
893 }
894 }
895 FLOPPY_DPRINTF("start non-DMA transfer\n");
896 /* IO based transfer: calculate len */
897 fdctrl_raise_irq(fdctrl, 0x00);
899 return;
900 }
902 /* Prepare a transfer of deleted data */
903 static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
904 {
905 /* We don't handle deleted data,
906 * so we don't return *ANYTHING*
907 */
908 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
909 }
911 /* handlers for DMA transfers */
912 static int fdctrl_transfer_handler (void *opaque, int nchan,
913 int dma_pos, int dma_len)
914 {
915 fdctrl_t *fdctrl;
916 fdrive_t *cur_drv;
917 int len, start_pos, rel_pos;
918 uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
920 fdctrl = opaque;
921 if (!(fdctrl->state & FD_CTRL_BUSY)) {
922 FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
923 return 0;
924 }
925 cur_drv = get_cur_drv(fdctrl);
926 if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
927 fdctrl->data_dir == FD_DIR_SCANH)
928 status2 = 0x04;
929 if (dma_len > fdctrl->data_len)
930 dma_len = fdctrl->data_len;
931 if (cur_drv->bs == NULL) {
932 if (fdctrl->data_dir == FD_DIR_WRITE)
933 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
934 else
935 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
936 len = 0;
937 goto transfer_error;
938 }
939 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
940 for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
941 len = dma_len - fdctrl->data_pos;
942 if (len + rel_pos > FD_SECTOR_LEN)
943 len = FD_SECTOR_LEN - rel_pos;
944 FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x "
945 "(%d-0x%08x 0x%08x)\n", len, size, fdctrl->data_pos,
946 fdctrl->data_len, fdctrl->cur_drv, cur_drv->head,
947 cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
948 fd_sector(cur_drv) * 512, addr);
949 if (fdctrl->data_dir != FD_DIR_WRITE ||
950 len < FD_SECTOR_LEN || rel_pos != 0) {
951 /* READ & SCAN commands and realign to a sector for WRITE */
952 if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
953 fdctrl->fifo, 1) < 0) {
954 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
955 fd_sector(cur_drv));
956 /* Sure, image size is too small... */
957 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
958 }
959 }
960 switch (fdctrl->data_dir) {
961 case FD_DIR_READ:
962 /* READ commands */
963 DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
964 fdctrl->data_pos, len);
965 /* cpu_physical_memory_write(addr + fdctrl->data_pos, */
966 /* fdctrl->fifo + rel_pos, len); */
967 break;
968 case FD_DIR_WRITE:
969 /* WRITE commands */
970 DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
971 fdctrl->data_pos, len);
972 /* cpu_physical_memory_read(addr + fdctrl->data_pos, */
973 /* fdctrl->fifo + rel_pos, len); */
974 if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
975 fdctrl->fifo, 1) < 0) {
976 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
977 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
978 goto transfer_error;
979 }
980 break;
981 default:
982 /* SCAN commands */
983 {
984 uint8_t tmpbuf[FD_SECTOR_LEN];
985 int ret;
986 DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
987 /* cpu_physical_memory_read(addr + fdctrl->data_pos, */
988 /* tmpbuf, len); */
989 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
990 if (ret == 0) {
991 status2 = 0x08;
992 goto end_transfer;
993 }
994 if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
995 (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
996 status2 = 0x00;
997 goto end_transfer;
998 }
999 }
1000 break;
1002 fdctrl->data_pos += len;
1003 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1004 if (rel_pos == 0) {
1005 /* Seek to next sector */
1006 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
1007 cur_drv->head, cur_drv->track, cur_drv->sect,
1008 fd_sector(cur_drv),
1009 fdctrl->data_pos - size);
1010 /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
1011 error in fact */
1012 if (cur_drv->sect >= cur_drv->last_sect ||
1013 cur_drv->sect == fdctrl->eot) {
1014 cur_drv->sect = 1;
1015 if (FD_MULTI_TRACK(fdctrl->data_state)) {
1016 if (cur_drv->head == 0 &&
1017 (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1018 cur_drv->head = 1;
1019 } else {
1020 cur_drv->head = 0;
1021 cur_drv->track++;
1022 if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
1023 break;
1025 } else {
1026 cur_drv->track++;
1027 break;
1029 FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
1030 cur_drv->head, cur_drv->track,
1031 cur_drv->sect, fd_sector(cur_drv));
1032 } else {
1033 cur_drv->sect++;
1037 end_transfer:
1038 len = fdctrl->data_pos - start_pos;
1039 FLOPPY_DPRINTF("end transfer %d %d %d\n",
1040 fdctrl->data_pos, len, fdctrl->data_len);
1041 if (fdctrl->data_dir == FD_DIR_SCANE ||
1042 fdctrl->data_dir == FD_DIR_SCANL ||
1043 fdctrl->data_dir == FD_DIR_SCANH)
1044 status2 = 0x08;
1045 if (FD_DID_SEEK(fdctrl->data_state))
1046 status0 |= 0x20;
1047 fdctrl->data_len -= len;
1048 // if (fdctrl->data_len == 0)
1049 fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1050 transfer_error:
1052 return len;
1055 /* Data register : 0x05 */
1056 static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
1058 fdrive_t *cur_drv;
1059 uint32_t retval = 0;
1060 int pos, len;
1062 cur_drv = get_cur_drv(fdctrl);
1063 fdctrl->state &= ~FD_CTRL_SLEEP;
1064 if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) {
1065 FLOPPY_ERROR("can't read data in CMD state\n");
1066 return 0;
1068 pos = fdctrl->data_pos;
1069 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1070 pos %= FD_SECTOR_LEN;
1071 if (pos == 0) {
1072 len = fdctrl->data_len - fdctrl->data_pos;
1073 if (len > FD_SECTOR_LEN)
1074 len = FD_SECTOR_LEN;
1075 bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1076 fdctrl->fifo, len);
1079 retval = fdctrl->fifo[pos];
1080 if (++fdctrl->data_pos == fdctrl->data_len) {
1081 fdctrl->data_pos = 0;
1082 /* Switch from transfer mode to status mode
1083 * then from status mode to command mode
1084 */
1085 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1086 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1087 } else {
1088 fdctrl_reset_fifo(fdctrl);
1089 fdctrl_reset_irq(fdctrl);
1092 FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
1094 return retval;
1097 static void fdctrl_format_sector (fdctrl_t *fdctrl)
1099 fdrive_t *cur_drv;
1100 uint8_t kh, kt, ks;
1101 int did_seek;
1103 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1104 cur_drv = get_cur_drv(fdctrl);
1105 kt = fdctrl->fifo[6];
1106 kh = fdctrl->fifo[7];
1107 ks = fdctrl->fifo[8];
1108 FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
1109 fdctrl->cur_drv, kh, kt, ks,
1110 _fd_sector(kh, kt, ks, cur_drv->last_sect));
1111 did_seek = 0;
1112 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
1113 case 2:
1114 /* sect too big */
1115 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1116 fdctrl->fifo[3] = kt;
1117 fdctrl->fifo[4] = kh;
1118 fdctrl->fifo[5] = ks;
1119 return;
1120 case 3:
1121 /* track too big */
1122 fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
1123 fdctrl->fifo[3] = kt;
1124 fdctrl->fifo[4] = kh;
1125 fdctrl->fifo[5] = ks;
1126 return;
1127 case 4:
1128 /* No seek enabled */
1129 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1130 fdctrl->fifo[3] = kt;
1131 fdctrl->fifo[4] = kh;
1132 fdctrl->fifo[5] = ks;
1133 return;
1134 case 1:
1135 did_seek = 1;
1136 fdctrl->data_state |= FD_STATE_SEEK;
1137 break;
1138 default:
1139 break;
1141 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1142 if (cur_drv->bs == NULL ||
1143 bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
1144 FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv));
1145 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1146 } else {
1147 if (cur_drv->sect == cur_drv->last_sect) {
1148 fdctrl->data_state &= ~FD_STATE_FORMAT;
1149 /* Last sector done */
1150 if (FD_DID_SEEK(fdctrl->data_state))
1151 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1152 else
1153 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1154 } else {
1155 /* More to do */
1156 fdctrl->data_pos = 0;
1157 fdctrl->data_len = 4;
1162 static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
1164 fdrive_t *cur_drv;
1166 cur_drv = get_cur_drv(fdctrl);
1167 /* Reset mode */
1168 if (fdctrl->state & FD_CTRL_RESET) {
1169 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
1170 return;
1172 fdctrl->state &= ~FD_CTRL_SLEEP;
1173 if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) {
1174 FLOPPY_ERROR("can't write data in status mode\n");
1175 return;
1177 /* Is it write command time ? */
1178 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1179 /* FIFO data write */
1180 fdctrl->fifo[fdctrl->data_pos++] = value;
1181 if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
1182 fdctrl->data_pos == fdctrl->data_len) {
1183 bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1184 fdctrl->fifo, FD_SECTOR_LEN);
1186 /* Switch from transfer mode to status mode
1187 * then from status mode to command mode
1188 */
1189 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA)
1190 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1191 return;
1193 if (fdctrl->data_pos == 0) {
1194 /* Command */
1195 switch (value & 0x5F) {
1196 case 0x46:
1197 /* READ variants */
1198 FLOPPY_DPRINTF("READ command\n");
1199 /* 8 parameters cmd */
1200 fdctrl->data_len = 9;
1201 goto enqueue;
1202 case 0x4C:
1203 /* READ_DELETED variants */
1204 FLOPPY_DPRINTF("READ_DELETED command\n");
1205 /* 8 parameters cmd */
1206 fdctrl->data_len = 9;
1207 goto enqueue;
1208 case 0x50:
1209 /* SCAN_EQUAL variants */
1210 FLOPPY_DPRINTF("SCAN_EQUAL command\n");
1211 /* 8 parameters cmd */
1212 fdctrl->data_len = 9;
1213 goto enqueue;
1214 case 0x56:
1215 /* VERIFY variants */
1216 FLOPPY_DPRINTF("VERIFY command\n");
1217 /* 8 parameters cmd */
1218 fdctrl->data_len = 9;
1219 goto enqueue;
1220 case 0x59:
1221 /* SCAN_LOW_OR_EQUAL variants */
1222 FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n");
1223 /* 8 parameters cmd */
1224 fdctrl->data_len = 9;
1225 goto enqueue;
1226 case 0x5D:
1227 /* SCAN_HIGH_OR_EQUAL variants */
1228 FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n");
1229 /* 8 parameters cmd */
1230 fdctrl->data_len = 9;
1231 goto enqueue;
1232 default:
1233 break;
1235 switch (value & 0x7F) {
1236 case 0x45:
1237 /* WRITE variants */
1238 FLOPPY_DPRINTF("WRITE command\n");
1239 /* 8 parameters cmd */
1240 fdctrl->data_len = 9;
1241 goto enqueue;
1242 case 0x49:
1243 /* WRITE_DELETED variants */
1244 FLOPPY_DPRINTF("WRITE_DELETED command\n");
1245 /* 8 parameters cmd */
1246 fdctrl->data_len = 9;
1247 goto enqueue;
1248 default:
1249 break;
1251 switch (value) {
1252 case 0x03:
1253 /* SPECIFY */
1254 FLOPPY_DPRINTF("SPECIFY command\n");
1255 /* 1 parameter cmd */
1256 fdctrl->data_len = 3;
1257 goto enqueue;
1258 case 0x04:
1259 /* SENSE_DRIVE_STATUS */
1260 FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n");
1261 /* 1 parameter cmd */
1262 fdctrl->data_len = 2;
1263 goto enqueue;
1264 case 0x07:
1265 /* RECALIBRATE */
1266 FLOPPY_DPRINTF("RECALIBRATE command\n");
1267 /* 1 parameter cmd */
1268 fdctrl->data_len = 2;
1269 goto enqueue;
1270 case 0x08:
1271 /* SENSE_INTERRUPT_STATUS */
1272 FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n",
1273 fdctrl->int_status);
1274 /* No parameters cmd: returns status if no interrupt */
1275 #if 0
1276 fdctrl->fifo[0] =
1277 fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv;
1278 #else
1279 /* XXX: int_status handling is broken for read/write
1280 commands, so we do this hack. It should be suppressed
1281 ASAP */
1282 fdctrl->fifo[0] =
1283 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv;
1284 #endif
1285 fdctrl->fifo[1] = cur_drv->track;
1286 fdctrl_set_fifo(fdctrl, 2, 0);
1287 fdctrl_reset_irq(fdctrl);
1288 fdctrl->int_status = 0xC0;
1289 return;
1290 case 0x0E:
1291 /* DUMPREG */
1292 FLOPPY_DPRINTF("DUMPREG command\n");
1293 /* Drives position */
1294 fdctrl->fifo[0] = drv0(fdctrl)->track;
1295 fdctrl->fifo[1] = drv1(fdctrl)->track;
1296 fdctrl->fifo[2] = 0;
1297 fdctrl->fifo[3] = 0;
1298 /* timers */
1299 fdctrl->fifo[4] = fdctrl->timer0;
1300 fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en;
1301 fdctrl->fifo[6] = cur_drv->last_sect;
1302 fdctrl->fifo[7] = (fdctrl->lock << 7) |
1303 (cur_drv->perpendicular << 2);
1304 fdctrl->fifo[8] = fdctrl->config;
1305 fdctrl->fifo[9] = fdctrl->precomp_trk;
1306 fdctrl_set_fifo(fdctrl, 10, 0);
1307 return;
1308 case 0x0F:
1309 /* SEEK */
1310 FLOPPY_DPRINTF("SEEK command\n");
1311 /* 2 parameters cmd */
1312 fdctrl->data_len = 3;
1313 goto enqueue;
1314 case 0x10:
1315 /* VERSION */
1316 FLOPPY_DPRINTF("VERSION command\n");
1317 /* No parameters cmd */
1318 /* Controller's version */
1319 fdctrl->fifo[0] = fdctrl->version;
1320 fdctrl_set_fifo(fdctrl, 1, 1);
1321 return;
1322 case 0x12:
1323 /* PERPENDICULAR_MODE */
1324 FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n");
1325 /* 1 parameter cmd */
1326 fdctrl->data_len = 2;
1327 goto enqueue;
1328 case 0x13:
1329 /* CONFIGURE */
1330 FLOPPY_DPRINTF("CONFIGURE command\n");
1331 /* 3 parameters cmd */
1332 fdctrl->data_len = 4;
1333 goto enqueue;
1334 case 0x14:
1335 /* UNLOCK */
1336 FLOPPY_DPRINTF("UNLOCK command\n");
1337 /* No parameters cmd */
1338 fdctrl->lock = 0;
1339 fdctrl->fifo[0] = 0;
1340 fdctrl_set_fifo(fdctrl, 1, 0);
1341 return;
1342 case 0x17:
1343 /* POWERDOWN_MODE */
1344 FLOPPY_DPRINTF("POWERDOWN_MODE command\n");
1345 /* 2 parameters cmd */
1346 fdctrl->data_len = 3;
1347 goto enqueue;
1348 case 0x18:
1349 /* PART_ID */
1350 FLOPPY_DPRINTF("PART_ID command\n");
1351 /* No parameters cmd */
1352 fdctrl->fifo[0] = 0x41; /* Stepping 1 */
1353 fdctrl_set_fifo(fdctrl, 1, 0);
1354 return;
1355 case 0x2C:
1356 /* SAVE */
1357 FLOPPY_DPRINTF("SAVE command\n");
1358 /* No parameters cmd */
1359 fdctrl->fifo[0] = 0;
1360 fdctrl->fifo[1] = 0;
1361 /* Drives position */
1362 fdctrl->fifo[2] = drv0(fdctrl)->track;
1363 fdctrl->fifo[3] = drv1(fdctrl)->track;
1364 fdctrl->fifo[4] = 0;
1365 fdctrl->fifo[5] = 0;
1366 /* timers */
1367 fdctrl->fifo[6] = fdctrl->timer0;
1368 fdctrl->fifo[7] = fdctrl->timer1;
1369 fdctrl->fifo[8] = cur_drv->last_sect;
1370 fdctrl->fifo[9] = (fdctrl->lock << 7) |
1371 (cur_drv->perpendicular << 2);
1372 fdctrl->fifo[10] = fdctrl->config;
1373 fdctrl->fifo[11] = fdctrl->precomp_trk;
1374 fdctrl->fifo[12] = fdctrl->pwrd;
1375 fdctrl->fifo[13] = 0;
1376 fdctrl->fifo[14] = 0;
1377 fdctrl_set_fifo(fdctrl, 15, 1);
1378 return;
1379 case 0x33:
1380 /* OPTION */
1381 FLOPPY_DPRINTF("OPTION command\n");
1382 /* 1 parameter cmd */
1383 fdctrl->data_len = 2;
1384 goto enqueue;
1385 case 0x42:
1386 /* READ_TRACK */
1387 FLOPPY_DPRINTF("READ_TRACK command\n");
1388 /* 8 parameters cmd */
1389 fdctrl->data_len = 9;
1390 goto enqueue;
1391 case 0x4A:
1392 /* READ_ID */
1393 FLOPPY_DPRINTF("READ_ID command\n");
1394 /* 1 parameter cmd */
1395 fdctrl->data_len = 2;
1396 goto enqueue;
1397 case 0x4C:
1398 /* RESTORE */
1399 FLOPPY_DPRINTF("RESTORE command\n");
1400 /* 17 parameters cmd */
1401 fdctrl->data_len = 18;
1402 goto enqueue;
1403 case 0x4D:
1404 /* FORMAT_TRACK */
1405 FLOPPY_DPRINTF("FORMAT_TRACK command\n");
1406 /* 5 parameters cmd */
1407 fdctrl->data_len = 6;
1408 goto enqueue;
1409 case 0x8E:
1410 /* DRIVE_SPECIFICATION_COMMAND */
1411 FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n");
1412 /* 5 parameters cmd */
1413 fdctrl->data_len = 6;
1414 goto enqueue;
1415 case 0x8F:
1416 /* RELATIVE_SEEK_OUT */
1417 FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n");
1418 /* 2 parameters cmd */
1419 fdctrl->data_len = 3;
1420 goto enqueue;
1421 case 0x94:
1422 /* LOCK */
1423 FLOPPY_DPRINTF("LOCK command\n");
1424 /* No parameters cmd */
1425 fdctrl->lock = 1;
1426 fdctrl->fifo[0] = 0x10;
1427 fdctrl_set_fifo(fdctrl, 1, 1);
1428 return;
1429 case 0xCD:
1430 /* FORMAT_AND_WRITE */
1431 FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n");
1432 /* 10 parameters cmd */
1433 fdctrl->data_len = 11;
1434 goto enqueue;
1435 case 0xCF:
1436 /* RELATIVE_SEEK_IN */
1437 FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n");
1438 /* 2 parameters cmd */
1439 fdctrl->data_len = 3;
1440 goto enqueue;
1441 default:
1442 /* Unknown command */
1443 FLOPPY_ERROR("unknown command: 0x%02x\n", value);
1444 fdctrl_unimplemented(fdctrl);
1445 return;
1448 enqueue:
1449 FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
1450 fdctrl->fifo[fdctrl->data_pos] = value;
1451 if (++fdctrl->data_pos == fdctrl->data_len) {
1452 /* We now have all parameters
1453 * and will be able to treat the command
1454 */
1455 if (fdctrl->data_state & FD_STATE_FORMAT) {
1456 fdctrl_format_sector(fdctrl);
1457 return;
1459 switch (fdctrl->fifo[0] & 0x1F) {
1460 case 0x06:
1462 /* READ variants */
1463 FLOPPY_DPRINTF("treat READ command\n");
1464 fdctrl_start_transfer(fdctrl, FD_DIR_READ);
1465 return;
1467 case 0x0C:
1468 /* READ_DELETED variants */
1469 // FLOPPY_DPRINTF("treat READ_DELETED command\n");
1470 FLOPPY_ERROR("treat READ_DELETED command\n");
1471 fdctrl_start_transfer_del(fdctrl, FD_DIR_READ);
1472 return;
1473 case 0x16:
1474 /* VERIFY variants */
1475 // FLOPPY_DPRINTF("treat VERIFY command\n");
1476 FLOPPY_ERROR("treat VERIFY command\n");
1477 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1478 return;
1479 case 0x10:
1480 /* SCAN_EQUAL variants */
1481 // FLOPPY_DPRINTF("treat SCAN_EQUAL command\n");
1482 FLOPPY_ERROR("treat SCAN_EQUAL command\n");
1483 fdctrl_start_transfer(fdctrl, FD_DIR_SCANE);
1484 return;
1485 case 0x19:
1486 /* SCAN_LOW_OR_EQUAL variants */
1487 // FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n");
1488 FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n");
1489 fdctrl_start_transfer(fdctrl, FD_DIR_SCANL);
1490 return;
1491 case 0x1D:
1492 /* SCAN_HIGH_OR_EQUAL variants */
1493 // FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n");
1494 FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n");
1495 fdctrl_start_transfer(fdctrl, FD_DIR_SCANH);
1496 return;
1497 default:
1498 break;
1500 switch (fdctrl->fifo[0] & 0x3F) {
1501 case 0x05:
1502 /* WRITE variants */
1503 FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]);
1504 fdctrl_start_transfer(fdctrl, FD_DIR_WRITE);
1505 return;
1506 case 0x09:
1507 /* WRITE_DELETED variants */
1508 // FLOPPY_DPRINTF("treat WRITE_DELETED command\n");
1509 FLOPPY_ERROR("treat WRITE_DELETED command\n");
1510 fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE);
1511 return;
1512 default:
1513 break;
1515 switch (fdctrl->fifo[0]) {
1516 case 0x03:
1517 /* SPECIFY */
1518 FLOPPY_DPRINTF("treat SPECIFY command\n");
1519 fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
1520 fdctrl->timer1 = fdctrl->fifo[2] >> 1;
1521 fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ;
1522 /* No result back */
1523 fdctrl_reset_fifo(fdctrl);
1524 break;
1525 case 0x04:
1526 /* SENSE_DRIVE_STATUS */
1527 FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n");
1528 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1529 cur_drv = get_cur_drv(fdctrl);
1530 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
1531 /* 1 Byte status back */
1532 fdctrl->fifo[0] = (cur_drv->ro << 6) |
1533 (cur_drv->track == 0 ? 0x10 : 0x00) |
1534 (cur_drv->head << 2) |
1535 fdctrl->cur_drv |
1536 0x28;
1537 fdctrl_set_fifo(fdctrl, 1, 0);
1538 break;
1539 case 0x07:
1540 /* RECALIBRATE */
1541 FLOPPY_DPRINTF("treat RECALIBRATE command\n");
1542 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1543 cur_drv = get_cur_drv(fdctrl);
1544 fd_recalibrate(cur_drv);
1545 fdctrl_reset_fifo(fdctrl);
1546 /* Raise Interrupt */
1547 fdctrl_raise_irq(fdctrl, 0x20);
1548 break;
1549 case 0x0F:
1550 /* SEEK */
1551 FLOPPY_DPRINTF("treat SEEK command\n");
1552 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1553 cur_drv = get_cur_drv(fdctrl);
1554 fd_start(cur_drv);
1555 if (fdctrl->fifo[2] <= cur_drv->track)
1556 cur_drv->dir = 1;
1557 else
1558 cur_drv->dir = 0;
1559 fdctrl_reset_fifo(fdctrl);
1560 if (fdctrl->fifo[2] > cur_drv->max_track) {
1561 fdctrl_raise_irq(fdctrl, 0x60);
1562 } else {
1563 cur_drv->track = fdctrl->fifo[2];
1564 /* Raise Interrupt */
1565 fdctrl_raise_irq(fdctrl, 0x20);
1567 break;
1568 case 0x12:
1569 /* PERPENDICULAR_MODE */
1570 FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n");
1571 if (fdctrl->fifo[1] & 0x80)
1572 cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
1573 /* No result back */
1574 fdctrl_reset_fifo(fdctrl);
1575 break;
1576 case 0x13:
1577 /* CONFIGURE */
1578 FLOPPY_DPRINTF("treat CONFIGURE command\n");
1579 fdctrl->config = fdctrl->fifo[2];
1580 fdctrl->precomp_trk = fdctrl->fifo[3];
1581 /* No result back */
1582 fdctrl_reset_fifo(fdctrl);
1583 break;
1584 case 0x17:
1585 /* POWERDOWN_MODE */
1586 FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n");
1587 fdctrl->pwrd = fdctrl->fifo[1];
1588 fdctrl->fifo[0] = fdctrl->fifo[1];
1589 fdctrl_set_fifo(fdctrl, 1, 1);
1590 break;
1591 case 0x33:
1592 /* OPTION */
1593 FLOPPY_DPRINTF("treat OPTION command\n");
1594 /* No result back */
1595 fdctrl_reset_fifo(fdctrl);
1596 break;
1597 case 0x42:
1598 /* READ_TRACK */
1599 // FLOPPY_DPRINTF("treat READ_TRACK command\n");
1600 FLOPPY_ERROR("treat READ_TRACK command\n");
1601 fdctrl_start_transfer(fdctrl, FD_DIR_READ);
1602 break;
1603 case 0x4A:
1604 /* READ_ID */
1605 FLOPPY_DPRINTF("treat READ_ID command\n");
1606 /* XXX: should set main status register to busy */
1607 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
1608 qemu_mod_timer(fdctrl->result_timer,
1609 qemu_get_clock(vm_clock) + (ticks_per_sec / 50));
1610 break;
1611 case 0x4C:
1612 /* RESTORE */
1613 FLOPPY_DPRINTF("treat RESTORE command\n");
1614 /* Drives position */
1615 drv0(fdctrl)->track = fdctrl->fifo[3];
1616 drv1(fdctrl)->track = fdctrl->fifo[4];
1617 /* timers */
1618 fdctrl->timer0 = fdctrl->fifo[7];
1619 fdctrl->timer1 = fdctrl->fifo[8];
1620 cur_drv->last_sect = fdctrl->fifo[9];
1621 fdctrl->lock = fdctrl->fifo[10] >> 7;
1622 cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
1623 fdctrl->config = fdctrl->fifo[11];
1624 fdctrl->precomp_trk = fdctrl->fifo[12];
1625 fdctrl->pwrd = fdctrl->fifo[13];
1626 fdctrl_reset_fifo(fdctrl);
1627 break;
1628 case 0x4D:
1629 /* FORMAT_TRACK */
1630 FLOPPY_DPRINTF("treat FORMAT_TRACK command\n");
1631 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1632 cur_drv = get_cur_drv(fdctrl);
1633 fdctrl->data_state |= FD_STATE_FORMAT;
1634 if (fdctrl->fifo[0] & 0x80)
1635 fdctrl->data_state |= FD_STATE_MULTI;
1636 else
1637 fdctrl->data_state &= ~FD_STATE_MULTI;
1638 fdctrl->data_state &= ~FD_STATE_SEEK;
1639 cur_drv->bps =
1640 fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
1641 #if 0
1642 cur_drv->last_sect =
1643 cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
1644 fdctrl->fifo[3] / 2;
1645 #else
1646 cur_drv->last_sect = fdctrl->fifo[3];
1647 #endif
1648 /* Bochs BIOS is buggy and don't send format informations
1649 * for each sector. So, pretend all's done right now...
1650 */
1651 fdctrl->data_state &= ~FD_STATE_FORMAT;
1652 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1653 break;
1654 case 0x8E:
1655 /* DRIVE_SPECIFICATION_COMMAND */
1656 FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n");
1657 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
1658 /* Command parameters done */
1659 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
1660 fdctrl->fifo[0] = fdctrl->fifo[1];
1661 fdctrl->fifo[2] = 0;
1662 fdctrl->fifo[3] = 0;
1663 fdctrl_set_fifo(fdctrl, 4, 1);
1664 } else {
1665 fdctrl_reset_fifo(fdctrl);
1667 } else if (fdctrl->data_len > 7) {
1668 /* ERROR */
1669 fdctrl->fifo[0] = 0x80 |
1670 (cur_drv->head << 2) | fdctrl->cur_drv;
1671 fdctrl_set_fifo(fdctrl, 1, 1);
1673 break;
1674 case 0x8F:
1675 /* RELATIVE_SEEK_OUT */
1676 FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n");
1677 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1678 cur_drv = get_cur_drv(fdctrl);
1679 fd_start(cur_drv);
1680 cur_drv->dir = 0;
1681 if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
1682 cur_drv->track = cur_drv->max_track - 1;
1683 } else {
1684 cur_drv->track += fdctrl->fifo[2];
1686 fdctrl_reset_fifo(fdctrl);
1687 fdctrl_raise_irq(fdctrl, 0x20);
1688 break;
1689 case 0xCD:
1690 /* FORMAT_AND_WRITE */
1691 // FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n");
1692 FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n");
1693 fdctrl_unimplemented(fdctrl);
1694 break;
1695 case 0xCF:
1696 /* RELATIVE_SEEK_IN */
1697 FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n");
1698 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1699 cur_drv = get_cur_drv(fdctrl);
1700 fd_start(cur_drv);
1701 cur_drv->dir = 1;
1702 if (fdctrl->fifo[2] > cur_drv->track) {
1703 cur_drv->track = 0;
1704 } else {
1705 cur_drv->track -= fdctrl->fifo[2];
1707 fdctrl_reset_fifo(fdctrl);
1708 /* Raise Interrupt */
1709 fdctrl_raise_irq(fdctrl, 0x20);
1710 break;
1715 static void fdctrl_result_timer(void *opaque)
1717 fdctrl_t *fdctrl = opaque;
1718 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);